From 75ddb137d445b3c90f2fa1de3c675144d861067b Mon Sep 17 00:00:00 2001 From: kowshik Date: Sun, 23 Feb 2025 02:09:36 +0600 Subject: [PATCH] First Commit --- README.md | 64 ++++++++++++ .../dataset/finetuning-dataset.jsonl | 92 ++++++++++++++++++ ai-finetuning/src/__init__.py | 1 + ai-finetuning/src/finetuning.py | 1 + ai-finetuning/src/inference.py | 1 + sample_flow_ai_design.png | Bin 0 -> 1640054 bytes sample_flow_ai_finetune.png | Bin 0 -> 456761 bytes 7 files changed, 159 insertions(+) create mode 100644 README.md create mode 100644 ai-finetuning/dataset/finetuning-dataset.jsonl create mode 100644 ai-finetuning/src/__init__.py create mode 100644 ai-finetuning/src/finetuning.py create mode 100644 ai-finetuning/src/inference.py create mode 100644 sample_flow_ai_design.png create mode 100644 sample_flow_ai_finetune.png diff --git a/README.md b/README.md new file mode 100644 index 0000000..df6ef51 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# AI Design & AI Fine-tuning + +This repository contains two taks: + +1. AI Design Analyzer - Convert wireframes/screenshots to detailed UI requirements and React code +2. BaaS(Backend As A Service) AI Fine-tuning - Fine-tune and generate backend endpoints based on existing schemas + +## Project Overview + +### AI Design Analyzer + +- Takes wireframe/website screenshots as input +- Analyzes and extracts UI requirements including: + - Typography + - Color schemes + - Button & Input styles + - Icons & Images + - Layout & Sections + - Interactive elements +- Generates production-ready React components + +### AI BaaS Endpoint Fine-tuner + +- Fine-tunes existing backend endpoints +- Generates new endpoints based on schema definitions +- Reuses existing services when applicable +- Creates new services only when necessary + +## 🚀 Getting Started + +### Prerequisites + +- Python 3.10+ + +## 📋 Model Training + +### AI Design Model + +The AI Design analyzer uses a model fine-tuned on UI wireframes and designs. The model is trained to detect: + +- UI elements and their relationships +- Style properties and patterns +- Layout structures +- Interactive components + + **Note**: Take any samples of data(Wireframe Image) and then do the above. For example: You can take example of UI from there: [https://dribbble.com/tags/ui](https://dribbble.com/tags/ui) + + ### Basically the flow will look like that: + + ![Sample Flow of AI Design](sample_flow_ai_design.png) + +### AI BaaS Fine-tuning Model + +The BaaS fine-tuning uses a CodeGen-based model trained to: + +- Understand existing endpoint patterns +- Recognize schema relationships +- Generate consistent API endpoints +- Maintain service architecture + **Note**: For fine-tuning use the data in the `/ai_finetunining/dataset` directory + +### The flow looks like this: + +![Sample flow of AI Finetuning](sample_flow_ai_finetune.png) diff --git a/ai-finetuning/dataset/finetuning-dataset.jsonl b/ai-finetuning/dataset/finetuning-dataset.jsonl new file mode 100644 index 0000000..f987ffe --- /dev/null +++ b/ai-finetuning/dataset/finetuning-dataset.jsonl @@ -0,0 +1,92 @@ +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/admin/register', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Validation\n- Register user\n- Create refresh token if asked"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate the input data. Then register the user with customRegister method. Generate the token and return the response\n Used services: - logService.log\n- JwtService.createAccessToken\n- sdk\n The code with comments is:\n ```\n const needRefreshToken = req.body.is_refresh ? true : false;\n let refreshToken = undefined;\n\n // Validation\n if (req.role !== \"admin\")\n return res.status(403).json({\n error: true,\n message: \"You are not authorized\"\n // validation: [{ field: \"\", message: \"You are not authorized\" }]\n });\n\n if (!req.body.email) {\n return res.status(403).json({\n error: true,\n message: \"Email Missing\",\n validation: [{ field: \"email\", message: \"Email missing\" }]\n });\n }\n if (!req.body.role) {\n return res.status(403).json({\n error: true,\n message: \"Role Missing\",\n validation: [{ field: \"role\", message: \"role missing\" }]\n });\n }\n if (req.body.status === undefined) {\n return res.status(403).json({\n error: true,\n message: \"Status Missing\",\n validation: [{ field: \"status\", message: \"status missing\" }]\n });\n }\n if (!req.body.password) {\n return res.status(403).json({\n error: true,\n message: \"Password Missing\",\n validation: [{ field: \"password\", message: \"Password missing\" }]\n });\n }\n\n logService.log(req.projectId, req.body);\n\n // Register user\n const result = await customRegister(req.sdk, req.projectId, req.body);\n\n if (typeof result == \"string\") {\n return res.status(403).json({\n error: true,\n message: result\n });\n } else {\n // create refresh token if asked\n if (needRefreshToken) {\n const refreshToken = JwtService.createAccessToken(\n {\n user_id: result.id,\n role: req.body.role\n },\n config.refresh_jwt_expire,\n config.jwt_key\n );\n let expireDate = new Date();\n expireDate.setSeconds(expireDate.getSeconds() + config.refresh_jwt_expire);\n await service.saveRefreshToken(app.get(\"sdk\"), req.projectId, result.id, refreshToken, expireDate);\n }\n\n return res.status(200).json({\n error: false,\n role: req.body.role,\n token: JwtService.createAccessToken(\n {\n user_id: result,\n role: req.body.role\n },\n config.jwt_expire,\n config.jwt_key\n ),\n refresh_token: refreshToken,\n expire_at: config.jwt_expire,\n user_id: result\n });\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/admin/register/:user/:lawfirmId', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - validate user data\n- register user\n- store in db"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate user data, register user and store the infor in user\n Used services: - sdk\n- sdk.setProjectId\n- sdk.insert\n The code with comments is:\n ```\n // validate user data\n if ((req.params.user !== \"client\" && req.params.user !== \"attorney\") || req.params.user !== req.body.role)\n return res.status(403).json({\n error: true,\n message: \"Invalid User\"\n });\n\n if (req.body.role === \"admin\" || req.body.role === \"lawfirm\")\n return res.status(403).json({\n error: true,\n message: `${req.body.role} cannot be registered`\n });\n\n if (!req.body.email) {\n return res.status(403).json({\n error: true,\n message: \"Email Missing\",\n validation: [{ field: \"email\", message: \"Email missing\" }]\n });\n }\n if (!req.body.role) {\n return res.status(403).json({\n error: true,\n message: \"Role Missing\",\n validation: [{ field: \"role\", message: \"role missing\" }]\n });\n }\n if (req.body.status === undefined) {\n return res.status(403).json({\n error: true,\n message: \"Status Missing\",\n validation: [{ field: \"status\", message: \"status missing\" }]\n });\n }\n if (!req.body.password) {\n return res.status(403).json({\n error: true,\n message: \"Password Missing\",\n validation: [{ field: \"password\", message: \"Password missing\" }]\n });\n }\n\n // register user\n const result = await customRegister(req.sdk, req.projectId, req.body);\n\n if (typeof result === \"string\") {\n return res.status(403).json({\n error: true,\n message: result\n });\n }\n\n // store in db\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n if (req.params.user === \"client\") {\n sdk.setTable(\"law_firm_client\");\n await sdk.insert({\n law_firm_id: req.params.lawfirmId,\n client_id: result,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n }\n\n if (req.params.user === \"attorney\") {\n sdk.setTable(\"law_firm_attorney\");\n await sdk.insert({\n law_firm_id: req.params.lawfirmId,\n attorney_id: result,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n }\n\n return res.status(201).json({\n error: false,\n message: result\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/admin/get-user', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - validation\n- get users"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Vaidate input, fetch users form db and return response\n Used services: - logService.log\n- sdk\n The code with comments is:\n ```\n // validation\n if (!req.body.id) {\n return res.status(403).json({\n error: true,\n message: \"ID is Missing\",\n validation: [{ field: \"id\", message: \"ID is Missing\" }]\n });\n }\n\n logService.log(req.projectId, req.body.id);\n\n // get users\n const result = await customGet(req.sdk, req.projectId, req.body.id);\n\n if (Object.keys(result).length === 0)\n return res.status(404).json({\n error: true,\n message: \"Not Found\"\n });\n\n return res.status(200).json({\n error: false,\n ...result\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/lawfirm/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Fetch lawfirms"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Apply the query to db to get lawfirms with pagination.\n Used services: \n The code with comments is:\n ```\n // Fetch lawfirms\n const commonSql = `FROM forkfirm_user LEFT JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.law_firm_id = forkfirm_user.id LEFT JOIN forkfirm_profile ON forkfirm_user.id = forkfirm_profile.user_id WHERE forkfirm_user.role = \"lawfirm\" AND `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_user.*, forkfirm_profile.website AS website, forkfirm_profile.base_office AS base_office, (SELECT COUNT(forkfirm_law_firm_attorney.law_firm_id)) AS attorneys ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n req.body.groupBy = \" forkfirm_user.id \";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/client/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - get clients\n- get doc rates\n- get subclients\n- get law_firm"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Baed on the url param, \"method\" apply query for fetching clients or docs rates or subclients or lawfirm with pagination.\n Used services: \n The code with comments is:\n ```\n const commonSql = `FROM forkfirm_user LEFT JOIN forkfirm_profile ON forkfirm_user.id = forkfirm_profile.user_id LEFT JOIN forkfirm_client_tag ON forkfirm_client_tag.client_id = forkfirm_user.id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_client_tag.tag_id LEFT JOIN forkfirm_photo ON forkfirm_photo.id = forkfirm_profile.playbook WHERE forkfirm_user.role = 'client' AND `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_user.*, invited_by, forkfirm_profile.id AS profile_id, forkfirm_tag.name AS tag_name, url ${commonSql}`;\n\n // get clients\n if (req.params.method === \"PAGINATE\") {\n req.body.groupBy = \" forkfirm_user.id \";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get doc rates\n if (req.params.method === \"view-rates\") {\n const commonSql = `FROM forkfirm_user JOIN forkfirm_doc_type LEFT JOIN forkfirm_client_rate ON forkfirm_client_rate.doc_type_id = forkfirm_doc_type.id AND forkfirm_client_rate.client_id = forkfirm_user.id WHERE forkfirm_user.role = \"client\" AND `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_user.*, forkfirm_client_rate.id AS client_rate_id, rate, name AS doc_name ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get subclients\n if (req.params.method === \"subclient\") {\n const commonSql = `FROM forkfirm_user AS subclient LEFT JOIN forkfirm_client_subclient ON forkfirm_client_subclient.subclient_id = subclient.id LEFT JOIN forkfirm_user AS client ON client.id = forkfirm_client_subclient.client_id LEFT JOIN forkfirm_law_firm_client ON forkfirm_law_firm_client.client_id = client.id WHERE subclient.role = 'subclient' AND `;\n\n const sqlCount = `SELECT COUNT(*) OVER() AS num ${commonSql}`;\n\n const sqlSelect = `SELECT subclient.*, client.id AS client_id, client.first_name AS client_first_name, client.last_name AS client_last_name, client.email AS client_email, forkfirm_client_subclient.id AS forkfirm_client_subclient_id, show_billing, law_firm_id, JSON_ARRAYAGG(law_firm_id) AS all_lawfirms ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get law_firm\n if (req.params.method === \"view-law-firms\") {\n const tables = [\"user\", \"law_firm_client\"];\n\n const commonSql = `FROM forkfirm_user LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = forkfirm_user.id LEFT JOIN forkfirm_photo ON forkfirm_photo.id = forkfirm_profile.playbook LEFT JOIN forkfirm_law_firm_client ON forkfirm_user.id = forkfirm_law_firm_client.client_id LEFT JOIN forkfirm_user AS law_firm ON law_firm.id = forkfirm_law_firm_client.law_firm_id WHERE forkfirm_user.role = \"client\" AND `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_user.*, law_firm.first_name AS law_firm_first_name, law_firm.last_name AS law_firm_last_name, law_firm.id AS current_law_firm_id, forkfirm_law_firm_client.id AS law_firm_client_id, url AS playbook_url, invited_by ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/invoice/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - fetch invoices\n- export invoices"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n If the method is export, export the invoice. If paginate then get invoices with pagination.\n Used services: \n The code with comments is:\n ```\n const tables = [\"invoice\", \"user\"];\n const joinFields = [\"id\", \"client_id\"];\n const renamedTables = tables.map((table) => `forkfirm_${table}`);\n\n const sqlCount = `SELECT COUNT(*) as num FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n const sqlSelect = `SELECT forkfirm_invoice.*, forkfirm_user.email, forkfirm_user.first_name, forkfirm_user.last_name, CASE WHEN forkfirm_invoice.status = 0 THEN 'unpaid' WHEN forkfirm_invoice.status = 1 THEN 'paid' ELSE 'void' END AS status_string FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n // fetch invoices\n if (req.params.method === \"PAGINATE\") {\n const { date } = req.body;\n date ? req.body.where.push(`DATE_FORMAT(forkfirm_invoice.generated_at, '%Y-%m') = DATE_FORMAT('${date}', '%Y-%m')`) : \"\";\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // export invoices\n if (req.params.method === \"EXPORT\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/lawfirm-invoice/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - get lawfirm invoices\n- export lawfirm invoices"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n If the method is export, export the lawfirm invoice. If paginate then get lawfirm invoices with pagination.\n Used services: \n The code with comments is:\n ```\n const tables = [\"invoice_lawfirm\", \"user\"];\n const joinFields = [\"id\", \"lawfirm_id\"];\n const renamedTables = tables.map((table) => `forkfirm_${table}`);\n\n const sqlCount = `SELECT COUNT(*) as num FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n let sqlSelect = `SELECT forkfirm_invoice_lawfirm.*, email, first_name, last_name, CASE WHEN forkfirm_invoice_lawfirm.status = 0 THEN 'unpaid' WHEN forkfirm_invoice_lawfirm.status = 1 THEN 'paid' ELSE 'void' END AS status_string FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n // get lawfirm invoices\n if (req.params.method === \"PAGINATE\") {\n const { date } = req.body;\n date ? req.body.where.push(`DATE_FORMAT(forkfirm_invoice_lawfirm.generated_at, '%Y-%m') = DATE_FORMAT('${date}', '%Y-%m')`) : \"\";\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // export lawfirm invoices\n if (req.params.method === \"EXPORT\") {\n sqlSelect = `SELECT forkfirm_invoice_lawfirm.id, generated_at, lawfirm_id, email AS lawfirm_email, first_name AS lawfirm_name, charged_amount, fork_fee, payout_amount, CASE WHEN forkfirm_invoice_lawfirm.status = 0 THEN 'Unpaid' WHEN forkfirm_invoice_lawfirm.status = 1 THEN 'Paid' ELSE 'Void' END AS status, paid_at FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/contract/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - get contracts\n- get contract tags\n- get contract's docs"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n If method is paginate then it returns list of paginated contracts. If tag it returns contract's tags and for doc returns contract's docs. If export exports the contracts.\n Used services: \n The code with comments is:\n ```\n // get contracts\n req.body.groupBy = \"forkfirm_contract.id\";\n const commonSql = `FROM forkfirm_contract LEFT JOIN forkfirm_user AS client ON client.id = forkfirm_contract.client_id LEFT JOIN forkfirm_user AS attorney ON attorney.id = forkfirm_contract.attorney_id LEFT JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.attorney_id = attorney.id LEFT JOIN forkfirm_law_firm_client ON forkfirm_law_firm_client.client_id = client.id AND forkfirm_law_firm_attorney.law_firm_id = forkfirm_law_firm_client.law_firm_id LEFT JOIN forkfirm_user AS lawfirm ON lawfirm.id = forkfirm_law_firm_client.law_firm_id LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = lawfirm.id LEFT JOIN forkfirm_client_subclient ON forkfirm_client_subclient.client_id = client.id AND forkfirm_client_subclient.subclient_id = forkfirm_contract.originator LEFT JOIN forkfirm_user AS subclient ON subclient.id = forkfirm_client_subclient.subclient_id LEFT JOIN forkfirm_doc_type ON forkfirm_doc_type.id = forkfirm_contract.doc_type_id LEFT JOIN forkfirm_client_rate ON forkfirm_client_rate.doc_type_id = forkfirm_doc_type.id AND forkfirm_client_rate.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_client_hours ON forkfirm_client_hours.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_contract_tag ON forkfirm_contract_tag.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_contract_tag.tag_id LEFT JOIN forkfirm_client_tag ON forkfirm_client_tag.client_id = client.id LEFT JOIN forkfirm_tag AS client_tag ON client_tag.id = forkfirm_client_tag.tag_id WHERE `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract.*, lawfirm.first_name AS lawfirm_first_name, lawfirm.id AS lawfirm_id, lawfirm.last_name AS lawfirm_last_name, attorney.first_name AS attorney_first_name, attorney.last_name AS attorney_last_name, base_office AS lawfirm_office, client.first_name AS client_first_name, client.last_name AS client_last_name, client.email, hour, forkfirm_doc_type.name as doc_name, rate, IF(contract_type, rate * hour, rate) AS charged_amount, GROUP_CONCAT(DISTINCT CONCAT(client_tag.name, '|', client_tag.id, '|', client_tag.input_type, '|', client_tag.input_duration)) AS client_tag, GROUP_CONCAT(DISTINCT CONCAT(forkfirm_tag.name, '|', forkfirm_tag.id, '|', forkfirm_contract_tag.input_type, '|', COALESCE(forkfirm_contract_tag.input_duration,'null'))) AS contract_tag, subclient.id AS subclient_id ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get contract tags\n if (req.params.method === \"tag\") {\n req.params.method = \"PAGINATE\";\n req.body.groupBy = null;\n const commonSql = `FROM forkfirm_contract_tag LEFT JOIN forkfirm_contract ON forkfirm_contract.id = forkfirm_contract_tag.contract_id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_contract_tag.tag_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract_tag.*, name ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get contract's docs\n if (req.params.method === \"doc\") {\n req.params.method = \"PAGINATE\";\n req.body.groupBy = null;\n const commonSql = `FROM forkfirm_contract LEFT JOIN forkfirm_photo AS doc ON doc.id = forkfirm_contract.document_id LEFT JOIN forkfirm_photo AS doc2 ON doc2.id = forkfirm_contract.document_id2 LEFT JOIN forkfirm_photo AS doc3 ON doc3.id = forkfirm_contract.document_id3 LEFT JOIN forkfirm_photo AS executed_doc ON executed_doc.id = forkfirm_contract.executed_document_id LEFT JOIN forkfirm_photo AS executed_doc2 ON executed_doc2.id = forkfirm_contract.executed_document_id2 LEFT JOIN forkfirm_photo AS executed_doc3 ON executed_doc3.id = forkfirm_contract.executed_document_id3 WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract.id, doc.url AS doc_url, doc2.url AS doc2_url, doc3.url AS doc3_url, executed_doc.url AS executed_doc_url, executed_doc2.url AS executed_doc2_url, executed_doc3.url AS executed_doc3_url ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n if (req.params.method === \"EXPORT\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n return res.status(403).json({\n error: true,\n message: \"Invalid method\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/pending-contract/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - get pending contracts"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db for all pending contracts and returns them.\n Used services: \n The code with comments is:\n ```\n // get pending contracts\n req.body.groupBy = \"forkfirm_contract.id\";\n const commonSql = `FROM forkfirm_contract LEFT JOIN forkfirm_user AS client ON client.id = forkfirm_contract.client_id LEFT JOIN forkfirm_law_firm_client ON forkfirm_law_firm_client.client_id = client.id LEFT JOIN forkfirm_user AS attorney ON attorney.id = forkfirm_contract.attorney_id LEFT JOIN forkfirm_attorney_client ON forkfirm_attorney_client.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.attorney_id = forkfirm_contract.attorney_id LEFT JOIN forkfirm_user AS lawfirm ON lawfirm.id = forkfirm_law_firm_attorney.law_firm_id LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = forkfirm_law_firm_attorney.law_firm_id LEFT JOIN forkfirm_client_subclient ON forkfirm_client_subclient.client_id = forkfirm_contract.client_id AND forkfirm_client_subclient.subclient_id = forkfirm_contract.originator LEFT JOIN forkfirm_user AS subclient ON subclient.id = forkfirm_client_subclient.subclient_id LEFT JOIN forkfirm_doc_type ON forkfirm_doc_type.id = forkfirm_contract.doc_type_id LEFT JOIN forkfirm_client_rate ON forkfirm_client_rate.doc_type_id = forkfirm_doc_type.id AND forkfirm_client_rate.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_client_hours ON forkfirm_client_hours.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_contract_tag ON forkfirm_contract_tag.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_contract_tag.tag_id LEFT JOIN forkfirm_client_tag ON forkfirm_client_tag.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_tag AS client_tag ON client_tag.id = forkfirm_client_tag.tag_id WHERE `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract.*, lawfirm.first_name AS lawfirm_first_name, lawfirm.id AS lawfirm_id, lawfirm.last_name AS lawfirm_last_name, attorney.first_name AS attorney_first_name, attorney.last_name AS attorney_last_name, base_office AS lawfirm_office, client.first_name AS client_first_name, client.last_name AS client_last_name, client.email, hour, forkfirm_doc_type.name as doc_name, rate, IF(contract_type, rate * hour, rate) AS charged_amount, GROUP_CONCAT(DISTINCT CONCAT(client_tag.name, '|', client_tag.id, '|', client_tag.input_type, '|', client_tag.input_duration)) AS client_tag, subclient.id AS subclient_id ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n return res.status(403).json({\n error: true,\n message: \"Invalid method\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/tag', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - validate data\n- get client's tags"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate input data and fetch client's tags from db and return data\n Used services: \n The code with comments is:\n ```\n const { where, page, limit, sortId, direction } = req.body;\n\n // validate data\n if ((page && typeof page !== \"number\") || (limit && typeof limit !== \"number\")) return res.status(400).json({ error: true, message: \"Invalid Request\" });\n\n if (sortId)\n req.body.sortId = sortId\n .split(\".\")\n .map((item) => `\\`${item}\\``)\n .join(\".\");\n\n if (direction) {\n if (direction === \"DESC\") req.body.direction = \"DESC\";\n else req.body.direction = \"ASC\";\n }\n\n if (where) {\n const searchStr = getSearchStr(where);\n req.body.where = searchStr;\n }\n\n const commonSql = `FROM forkfirm_client_tag LEFT JOIN forkfirm_user ON forkfirm_user.id = forkfirm_client_tag.client_id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_client_tag.tag_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n // get client's tags\n const sqlSelect = `SELECT forkfirm_client_tag.*, first_name, last_name, forkfirm_tag.name AS tag_name, input_type, input_duration ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n acenanny_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_articles\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nthumbnail | varchar(255)\ntitle | varchar(255)\ncategory | int(11)\ntags | varchar(255)\ndescription | mediumtext\ncontent | mediumtext\n\nacenanny_background_check\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ntype | int(11)\nverification_id | varchar(255)\nstatus | varchar(255)\noutcome | varchar(255)\napplication_adjudication | varchar(255)\nreports | mediumtext\nverified_data | longtext\ndate | date\nlast_synced | datetime\n\nacenanny_blog_category\n----------------------\nName | Type\n\nid | int(11)\nparent_id | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_post\n------------------\nName | Type\n\nid | int(11)\ntitle | varchar(255)\ndescription | mediumtext\ncontent | longtext\nthumbnail | mediumtext\nauthor | int(11)\nmeta | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_post_category\n---------------------------\nName | Type\n\nid | int(11)\ncategory_id | int(11)\npost_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_post_tags\n-----------------------\nName | Type\n\nid | int(11)\ntag_id | int(11)\npost_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_post_views\n------------------------\nName | Type\n\nid | int(11)\npost_id | int(11)\nviewer_ip | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_tags\n------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_favourites\n-------------------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nnanny_id | int(11)\n\nacenanny_nanny_profile\n----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nfirst_name | mediumtext\nlast_name | mediumtext\nfirst_name_other_lang | mediumtext\nlast_name_other_lang | mediumtext\nweChat | mediumtext\nlanguages | mediumtext\ncountry | mediumtext\ncity | mediumtext\nregion | mediumtext\nbirthyear | mediumtext\nschool | mediumtext\ndegree | mediumtext\nstudy_field | mediumtext\nintro | longtext\nprofile_picture | mediumtext\nadditional_pictures | longtext\ninterview | longtext\ncertificates | longtext\nattribute_details | mediumtext\nnanny_type | mediumtext\nlocation_type | mediumtext\nlocations | mediumtext\nemployment_type | mediumtext\nstart_date | mediumtext\nschedule | longtext\nexperience_and_skills | longtext\nidentity_verification_id | longtext\nstatus | int(11)\naccount_status | int(11)\nsignup_stage | int(11)\nsubmitted | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_nanny_reviews\n----------------------\nName | Type\n\nid | int(11)\nnanny_id | int(11)\nuser_id | int(11)\nrating | int(11)\nflag | int(11)\ndescription | longtext\nreply | longtext\ndetailed_rating | longtext\ncreate_at | datetime\n\nacenanny_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_posts\n--------------\nName | Type\n\nid | int(11)\nstatus | tinyint(1)\ntype | text\ndata | text\nlinks | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\naddress | text\ncity | text\nzip | text\nstate | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_reports\n----------------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nnanny_id | int(11)\ndescription | mediumtext\n\nacenanny_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nacenanny_setting\n----------------\nName | Type\n\nid | int(11)\nsetting_key | varchar(255)\nsetting_value | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nacenanny_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_user_agreement\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nagreement_type | varchar(255)\nip_address | varchar(255)\nbrowser | varchar(255)\ncreate_at | datetime\n\nacenanny_verification_code\n--------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\nphone | varchar(255)\ncode | varchar(255)\ncreate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v3/api/custom/admin/nanny/profile/edit/:user_id', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n "}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n \n Used services: \n The code with comments is:\n ```\n let {\n email,\n phone,\n wechat,\n languages,\n country,\n city,\n region,\n birthyear,\n school,\n degree,\n study_field,\n intro,\n profile_picture,\n additional_pictures,\n certificates,\n attribute_details,\n interview,\n nanny_type,\n location_type,\n locations,\n employment_type,\n start_date,\n schedule,\n experience_and_skills,\n account_status,\n status,\n first_name_other_lang,\n last_name_other_lang\n } = req.body;\n\n let user_id = req.params.user_id;\n let sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n console.log(req.body);\n\n sdk.setTable(\"user\");\n\n let exist = [];\n\n if (email) {\n exist = await sdk.get({ email });\n\n if (exist.length) {\n if (exist[0].id != user_id) throw new Error(\"Email Already Exists\");\n }\n }\n\n if (phone) {\n exist = await sdk.get({ phone });\n\n if (exist.length) {\n if (exist[0].id != user_id) throw new Error(\"Phone Already Exists\");\n }\n }\n\n let user = await sdk.get({ id: user_id });\n\n await sdk.update(\n {\n email: email ?? user[0].email,\n phone: phone ?? user[0].phone\n },\n user_id\n );\n\n sdk.setTable(\"nanny_profile\");\n\n let profile = await sdk.get({ user_id });\n\n if (!profile.length) throw new Error(\"User Doesn't Exists!\");\n\n let data = {};\n\n nanny_type = nanny_type ?? JSON.parse(profile[0].nanny_type);\n\n if (experience_and_skills) {\n for (let x of nanny_type) {\n let slug = x.toLowerCase().replace(/ /g, \"_\");\n console.log(\"***\", slug);\n data[slug] = {\n experience: experience_and_skills[slug]?.experience ?? null,\n us_experience: experience_and_skills[slug]?.us_experience ?? null,\n documented_experience: experience_and_skills[slug]?.documented_experience ? experience_and_skills[slug]?.documented_experience : [],\n recommendations: experience_and_skills[slug]?.recommendations ? experience_and_skills[slug]?.recommendations : [],\n payment_options: experience_and_skills[slug]?.payment_options\n ? formatPaymentOptions(experience_and_skills[slug].payment_options)\n : formatPaymentOptions({})\n };\n }\n }\n\n sdk.setTable(\"nanny_profile\");\n\n await sdk.updateWhere(\n {\n wechat: wechat ?? profile[0].wechat,\n first_name_other_lang: first_name_other_lang ?? profile[0].first_name_other_lang,\n last_name_other_lang: last_name_other_lang ?? profile[0].last_name_other_lang,\n languages: languages ? JSON.stringify(languages) : profile[0].languages,\n country: country ?? profile[0].country,\n city: city ?? profile[0].city,\n interview: interview ?? profile[0].interview,\n region: region ?? profile[0].region,\n birthyear: birthyear ?? profile[0].birthyear,\n school: school ?? profile[0].school,\n degree: degree ?? profile[0].degree,\n study_field: study_field ?? profile[0].study_field,\n intro: intro ?? profile[0].intro,\n profile_picture: profile_picture ?? profile[0].profile_picture,\n additional_pictures: additional_pictures ? JSON.stringify(additional_pictures) : profile[0].additional_pictures,\n certificates: certificates ? JSON.stringify(certificates) : profile[0].certificates,\n attribute_details: attribute_details ? JSON.stringify(attribute_details) : profile[0].attribute_details,\n nanny_type: JSON.stringify(nanny_type),\n location_type: location_type ?? profile[0].location_type,\n locations: locations ? JSON.stringify(locations) : profile[0].locations,\n employment_type: employment_type ? JSON.stringify(employment_type) : profile[0].employment_type,\n start_date: start_date ? sqlDateFormat(new Date(start_date)) : profile[0].start_date,\n schedule: schedule ? shceduleFormatting(schedule) : profile[0].schedule,\n experience_and_skills: experience_and_skills ? JSON.stringify(data) : profile[0].experience_and_skills,\n account_status: account_status ?? profile[0].account_status,\n status: status ?? profile[0].status,\n update_at: sqlDateTimeFormat(new Date())\n },\n { user_id }\n );\n\n return res.json({\n error: false,\n message: \"Updated\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/lawfirm/invoice', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n "}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get invoices of a lawfirm.\n Used services: \n The code with comments is:\n ```\n const { law_firm_id, date } = req.body;\n if (!law_firm_id || typeof law_firm_id !== \"number\") return res.status(400).json({ error: true, message: \"Lawfirm ID is required/invalid\" });\n\n date ? req.body.where.push(`DATE_FORMAT(generated_at, '%Y-%M') = DATE_FORMAT('${date}', '%Y-%M')`) : \"\";\n\n const commonSql = `FROM forkfirm_invoice LEFT JOIN forkfirm_law_firm_client ON forkfirm_law_firm_client.client_id = forkfirm_invoice.client_id LEFT JOIN forkfirm_user ON forkfirm_user.id = forkfirm_invoice.client_id WHERE law_firm_id = ${law_firm_id} AND `;\n\n const sqlCount = `SELECT COUNT(*) OVER() AS num ${commonSql}`;\n const sqlSelect = `SELECT forkfirm_invoice.*, forkfirm_user.first_name, forkfirm_user.last_name ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/lawfirm/edit-rate', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n API to Edit Client's Rates"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Fetch the user from db and if it's lawfirm allow it to update the client's rate.\n Used services: - sdk\n- sdk.setProjectId\n- sdk.setTable\n- sdk.update\n The code with comments is:\n ```\n if (req.role !== \"lawfirm\") return res.status(403).json({ error: true, message: \"Unauthorized User\" });\n\n const { client_id, id, rate } = req.body;\n\n const sql = `SELECT forkfirm_user.* FROM forkfirm_profile LEFT JOIN forkfirm_user ON forkfirm_profile.invited_by = forkfirm_user.email WHERE forkfirm_profile.user_id = ${client_id} AND `;\n req.params.method = \"RAW\";\n const result = await handleReq(req, res, [], null, sql);\n\n if (result[0].role === \"admin\") return res.status(403).json({ error: true, message: \"You are not authorized to update this client's rate!\" });\n\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"client_rate\");\n await sdk.update({ rate }, id);\n\n res.json({ error: false, message: \"Updated Successfully!\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/client/invoice-details', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n API to Get Invoice Details"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get invoice details with pagination.\n Used services: \n The code with comments is:\n ```\n const { date } = req.body;\n if (!date) return res.status(400).json({ error: true, message: \"Date is required\" });\n\n const commonSql = `FROM forkfirm_contract LEFT JOIN forkfirm_user AS client ON client.id = forkfirm_contract.client_id JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.attorney_id = forkfirm_contract.attorney_id LEFT JOIN forkfirm_user AS lawfirm ON lawfirm.id = forkfirm_law_firm_attorney.law_firm_id LEFT JOIN forkfirm_doc_type ON forkfirm_doc_type.id = forkfirm_contract.doc_type_id LEFT JOIN forkfirm_client_rate ON forkfirm_client_rate.doc_type_id = forkfirm_doc_type.id AND forkfirm_client_rate.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_client_hours ON forkfirm_client_hours.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = lawfirm.id WHERE DATE_FORMAT(forkfirm_contract.create_at, '%Y-%m') = DATE_FORMAT(DATE_SUB('${date}', INTERVAL 0 MONTH), '%Y-%m') AND `;\n\n const sqlCount = `SELECT COUNT(*) AS num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract.*, lawfirm.first_name AS lawfirm_first_name, lawfirm.last_name AS lawfirm_last_name, lawfirm.email AS lawfirm_email, forkfirm_profile.base_office, client.first_name AS client_first_name, client.last_name AS client_last_name, client.email AS client_email, name, rate, hour, IF(contract_type, IF(delivery, rate * hour * ${localConfig.upCharge}, rate * hour + ${localConfig.standardCharge}), IF(delivery, rate * ${localConfig.upCharge}, rate + ${localConfig.standardCharge})) AS charged_amount, IF(contract_type, rate * hour, rate) AS total_rate ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/client/pay', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - API for Payment\n- Create payment intent\n- Store pi\n- Update invoice"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Gret invoice from db. Create payment intent then store the payment intent in db and update the invoice\n Used services: - sdk\n- stripe.paymentIntents\n- sdk\n- sdk.setProjectId\n- sdk.setTable\n- sdk.insert\n- sdk.setTable\n The code with comments is:\n ```\n const { invoice_id, payment_method } = req.body;\n\n if (!invoice_id || !payment_method) return res.status(400).json({ error: true, message: \"Invoice and Payment Method is required\" });\n\n const getInvoice = `SELECT forkfirm_invoice.* FROM forkfirm_invoice WHERE forkfirm_invoice.id = ${invoice_id} AND forkfirm_invoice.status = 0 AND `;\n req.params.method = \"RAW\";\n const invoiceResult = await handleReq(req, res, [], null, getInvoice);\n\n if (!invoiceResult.length) return res.status(400).json({ error: true, message: \"Invoice is not available\" });\n\n const userInfo = `SELECT id, stripe_uid FROM forkfirm_user WHERE forkfirm_user.id = ${invoiceResult[0].client_id} AND `;\n const userInfoResult = await handleReq(req, res, [], null, userInfo);\n\n // Create payment intent\n const paymentIntent = await stripe.paymentIntents.create({\n payment_method_types: [\"card\"],\n amount: invoiceResult[0].charged_amount * 100,\n currency: \"usd\",\n customer: userInfoResult[0].stripe_uid,\n payment_method: payment_method,\n off_session: true,\n confirm: true\n });\n\n if (paymentIntent.status !== \"succeeded\") return res.status(400).json({ error: true, message: \"Payment Failed!\" });\n\n if (paymentIntent.status === \"succeeded\") {\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n // Store pi\n sdk.setTable(\"stripe_payment_intent\");\n const stripeInvoiceResult = await sdk.insert({\n user_id: userInfoResult[0].id,\n stripe_id: paymentIntent.id,\n object: JSON.stringify(paymentIntent),\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n // Update invoice\n sdk.setTable(\"invoice\");\n await sdk.update(\n {\n status: 1,\n stripe_payment_intent_id: stripeInvoiceResult,\n paid_at: sqlDateTimeFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n },\n invoice_id\n );\n\n return res.status(200).json({ error: false, message: \"Payment Successful!\" });\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.get('/v2/api/forkfirm/get-cards/:userId', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - API to get Stripe cards"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n This api get's user's stripe uid from db and fetches all the cards of the user.\n Used services: - TokenMiddleware\n- ProjectMiddleware\n- HostMiddleware\n- sdk\n- sdk.get\n- stripeService.retrieveStripeCustomerAllCards\n The code with comments is:\n ```\n const sdk = req.sdk;\n const { after = null, before = null, limit = 10 } = req.query;\n const id = req.params.userId;\n\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"user\");\n\n const user = await sdk.get({ id: id });\n if (!user[0]) {\n return res.status(404).json({ error: true, message: \"User not found\" });\n }\n const customer_stripe_id = user[0].stripe_uid;\n if (!customer_stripe_id)\n return res.status(200).json({\n error: false,\n data: null,\n limit: limit\n });\n const cards = await stripeService.retrieveStripeCustomerAllCards({ customerId: customer_stripe_id, after, before, limit });\n\n res.status(200).json({\n error: false,\n data: cards,\n limit: limit\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/add-card/', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Add Stripe Cards"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get customer's stripe uid. If no stripe uid then create stripe customer with card else just add new card to the existing stripe customer.\n Used services: - sdk\n- ValidationService.validateObject\n- sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- stripeService.createStripeCustomerWithCard\n- sdk.update\n- stripeService.addNewCardToStripeCustomer\n The code with comments is:\n ```\n const sdk = req.sdk;\n const { sourceToken, userId } = req.body;\n\n const validationResult = await ValidationService.validateObject({ sourceToken: \"required\", userId: \"required\" }, { sourceToken, userId });\n if (validationResult.error) {\n return res.status(400).json(validationResult);\n }\n\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"user\");\n\n const user = await sdk.get({ id: userId });\n if (!user.length) {\n return res.status(404).json({ error: true, message: \"User not found\" });\n }\n\n const customerStripeId = user[0].stripe_uid;\n const metadata = {\n projectId: sdk.getProjectId()\n };\n if (!customerStripeId) {\n var customer = await stripeService.createStripeCustomerWithCard({\n email: user[0].email,\n tokenId: sourceToken,\n metadata\n });\n\n await sdk.update({ stripe_uid: customer.id }, user[0].id);\n } else {\n var customer = await stripeService.addNewCardToStripeCustomer({\n customerId: customerStripeId,\n tokenId: sourceToken,\n metadata\n });\n }\n\n res.status(200).json({\n error: false,\n model: customer,\n message: \"Customer card added successfully\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.delete('/v2/api/forkfirm/card/:userId/:id', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Delete Stripe Card"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Find customer's stripe uid from db and use it to delete crad from customer's stripe account\n Used services: - sdk\n- ValidationService.validateObject\n- sdk.get\n- stripeService.deleteStripeCustomerCard\n The code with comments is:\n ```\n // Delete Stripe Card\n const sdk = req.sdk;\n const { id: cardId, userId } = req.params;\n\n const validationResult = await ValidationService.validateObject({ cardId: \"required\" }, { cardId });\n if (validationResult.error) {\n return res.status(400).json(validationResult);\n }\n\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"user\");\n const user = await sdk.get({ id: userId });\n if (!user[0]) {\n return res.status(404).json({ error: true, message: \"User not found\" });\n }\n\n if (!user[0].stripe_uid) {\n return res.status(404).json({ error: true, message: \"User is not a stripe customer\" });\n }\n\n await stripeService.deleteStripeCustomerCard({\n customerId: user[0].stripe_uid,\n cardId: cardId\n });\n res.status(200).json({ error: false, message: \"Card deleted successfully\", isDeleted: true });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/attorney/client/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get Attorney's Clients"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get attornes of a clients with pagination\n Used services: \n The code with comments is:\n ```\n // Get Attorney's Clients\n const { where, page, limit, sortId, direction } = req.body;\n\n if ((page && typeof page !== \"number\") || (limit && typeof limit !== \"number\")) return res.status(400).json({ error: true, message: \"Invalid Request\" });\n\n if (sortId)\n req.body.sortId = sortId\n .split(\".\")\n .map((item) => `\\`${item}\\``)\n .join(\".\");\n\n if (direction) {\n if (direction === \"DESC\") req.body.direction = \"DESC\";\n else req.body.direction = \"ASC\";\n }\n\n if (where) {\n const searchStr = getSearchStr(where);\n req.body.where = searchStr;\n }\n\n const commonSql = `FROM forkfirm_attorney_client LEFT JOIN forkfirm_user AS client ON forkfirm_attorney_client.client_id = client.id LEFT JOIN forkfirm_user AS attorney ON attorney.id = forkfirm_attorney_client.attorney_id LEFT JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.attorney_id = forkfirm_attorney_client.attorney_id LEFT JOIN forkfirm_user AS lawfirm ON lawfirm.id = forkfirm_law_firm_attorney.law_firm_id LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = client.id LEFT JOIN forkfirm_photo ON forkfirm_photo.id = forkfirm_profile.playbook WHERE `;\n\n const sqlCount = `SELECT COUNT(*) AS num ${commonSql}`;\n\n const sqlSelect = `SELECT client.*, attorney.id AS attorney_id, lawfirm.id AS lawfirm_id, forkfirm_attorney_client.id AS forkfirm_attorney_client_id, url AS playbook, forkfirm_profile.id AS profile_id ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/attorney/project/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - API to Get Attorney's Projects"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get attorney's projects with pagination\n Used services: \n The code with comments is:\n ```\n // API to Get Attorney's Projects\n const { where, page, limit, sortId, direction } = req.body;\n\n if ((page && typeof page !== \"number\") || (limit && typeof limit !== \"number\")) return res.status(400).json({ error: true, message: \"Invalid Request\" });\n\n if (sortId)\n req.body.sortId = sortId\n .split(\".\")\n .map((item) => `\\`${item}\\``)\n .join(\".\");\n\n if (direction) {\n if (direction === \"DESC\") req.body.direction = \"DESC\";\n else req.body.direction = \"ASC\";\n }\n\n if (where) {\n const searchStr = getSearchStr(where);\n req.body.where = searchStr;\n }\n\n const commonSql = `FROM forkfirm_project LEFT JOIN forkfirm_user ON forkfirm_user.id = forkfirm_project.client_id LEFT JOIN forkfirm_attorney_client ON forkfirm_attorney_client.client_id = forkfirm_project.client_id AND forkfirm_project.law_firm_id = (SELECT law_firm_id FROM forkfirm_law_firm_attorney WHERE attorney_id = forkfirm_attorney_client.attorney_id) LEFT JOIN forkfirm_photo AS pb ON pb.id = forkfirm_project.playbook LEFT JOIN forkfirm_photo AS pb2 ON pb2.id = forkfirm_project.playbook2 LEFT JOIN forkfirm_photo AS pb3 ON pb3.id = forkfirm_project.playbook3 LEFT JOIN forkfirm_photo AS nd ON nd.id = forkfirm_project.nda LEFT JOIN forkfirm_photo AS nd2 ON nd2.id = forkfirm_project.nda2 LEFT JOIN forkfirm_photo AS nd3 ON nd3.id = forkfirm_project.nda3 WHERE `;\n\n const sqlCount = `SELECT COUNT(*) OVER() AS num ${commonSql}`;\n const sqlSelect = `SELECT forkfirm_project.*, first_name, last_name, pb.url AS playbook_url, pb2.url AS playbook2_url, pb3.url AS playbook3_url, nd.url AS nda_url, nd2.url AS nda2_url, nd3.url AS nda3_url ${commonSql}`; // Note: Client's name\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.get('/v2/api/forkfirm/attorney/contract/:contractId/:activityStatus', async function (req, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - API to Update Pending Contract"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Update the status of the pending contract\n Used services: - sdk\n- sdk.update\n- sdk.setProjectId\n- sdk.setTable\n The code with comments is:\n ```\n // API to Update Pending Contract\n const { contractId, activityStatus } = req.params;\n\n if ((contractId && typeof +contractId !== \"number\") || (activityStatus && typeof +activityStatus !== \"number\"))\n return res.status(400).json({ error: true, message: \"Invalid Request\" });\n\n const sdk = req.app.get(\"sdk\");\n sdk.setProjectId(\"forkfirm\");\n sdk.setTable(\"contract\");\n await sdk.update(\n {\n current_activity_status: +activityStatus,\n activity_status_updated_at: sqlDateTimeFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n },\n contractId\n );\n\n res.redirect(\"https://forkfirm.manaknightdigital.com/\");\n // return res.json({ error: false, message: \"Contract Activity Status Updated Successfully\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API to get Properties"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get properties with pagination\n Used services: \n The code with comments is:\n ```\n // Custom API to get Properties\n const tables = [\"property\", \"user\", \"property_spaces\"];\n const joinFields = [\"host_id\", \"id\", \"property_id\"];\n const renamedTables = tables.map((table) => `ergo_${table}`);\n\n const sqlCount = `SELECT COUNT(*) as num FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[1]}.${joinFields[1]} = ${renamedTables[0]}.${joinFields[0]} WHERE `;\n\n const sqlSelect = `SELECT ergo_property.*, email, (SELECT COALESCE(COUNT(ergo_property_spaces.id), 0) FROM ${renamedTables[2]} WHERE ${renamedTables[2]}.${joinFields[2]} = ${renamedTables[0]}.${joinFields[1]}) AS spaces FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[1]}.${joinFields[1]} = ${renamedTables[0]}.${joinFields[0]} WHERE `;\n\n return await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-spaces/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Spaces"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get property space with pagination if method is pagination. if method is post insert property space and update space count.\n Used services: - sdk\n- sdk.setProjectId\n- sdk.setTable\n- existPermission\n- sdk.get\n- sdk.insert\n The code with comments is:\n ```\n // Custom API for Property Spaces\n const tables = [\"property_spaces\", \"property\", \"spaces\"];\n const commonSql = `FROM ergo_property_spaces LEFT JOIN ergo_property ON ergo_property_spaces.property_id = ergo_property.id LEFT JOIN ergo_spaces ON ergo_property_spaces.space_id = ergo_spaces.id LEFT JOIN ergo_photo ON ergo_photo.id = ergo_property_spaces.default_image_id LEFT JOIN ergo_review ON ergo_review.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_user ON ergo_user.id = ergo_property.host_id LEFT JOIN ergo_property_spaces_schedule_template ON ergo_property_spaces_schedule_template.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_property_spaces_images ON ergo_property_spaces_images.property_spaces_id = ergo_property_spaces.id AND ergo_property_spaces.default_image_id = ergo_property_spaces_images.photo_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT ergo_property_spaces.*, ergo_spaces.category AS space_category, ergo_property.name AS property_name, ergo_photo.url AS default_image_url, AVG(ergo_review.space_rating) AS average_space_rating, schedule_template_id, email ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") return await handleReq(req, res, tables, sqlCount, sqlSelect, \" ergo_property_spaces.id \");\n\n const sdk = req.sdk;\n sdk.getDatabase();\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"permission\");\n const permission = await sdk.get({\n role: req.role\n });\n\n const acl = JSON.parse(permission[0].permission);\n\n switch (req.params.method) {\n case \"POST\":\n if (existPermission(acl, \"property_spaces\", \"POST\")) {\n const payload = await preProcessReq(req, res, sdk, acl, \"property_spaces\", \"POST\");\n if (!payload) return;\n\n // Part: Define Tax If Not Provided\n if (!payload.tax) {\n sdk.setTable(\"settings\");\n const [settings] = await sdk.get({\n key_name: \"tax\"\n });\n\n if (!settings)\n return res.status(400).json({\n error: true,\n message: \"Vat is not defined.\"\n });\n\n req.body.tax = settings.key_value;\n }\n\n const insertResult = await sdk.insert({\n ...payload,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n sdk.setTable(\"property\");\n req.params.method = \"RAW\";\n const rawQuery = `UPDATE ergo_property SET spaces = spaces + 1 WHERE id = ${payload.property_id} AND `;\n await handleReq(req, res, [\"property\"], null, rawQuery, null, QueryTypes.UPDATE);\n\n return res.status(200).json({\n error: false,\n message: insertResult\n });\n } else {\n return res.status(403).json({\n error: true,\n message: \"Invalid Resource\"\n });\n }\n break;\n\n default:\n res.status(403);\n res.json({\n error: true,\n message: \"Invalid Method\"\n });\n break;\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-space-images', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Space Images"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get property space images from db\n Used services: - ProjectMiddleware\n- UrlMiddleware\n- HostMiddleware\n The code with comments is:\n ```\n // Custom API for Property Space Images\n const tables = [\"property_spaces_images\", \"property\", \"spaces\", \"photo\", \"property_spaces\"];\n\n const commonSql = `FROM ergo_property_spaces_images LEFT JOIN ergo_property ON ergo_property_spaces_images.property_id = ergo_property.id LEFT JOIN ergo_property_spaces ON ergo_property_spaces_images.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_spaces ON ergo_spaces.id = ergo_property_spaces.space_id LEFT JOIN ergo_photo ON ergo_property_spaces_images.photo_id = ergo_photo.id LEFT JOIN ergo_user ON ergo_user.id = ergo_property.host_id LEFT JOIN ergo_profile ON ergo_profile.user_id = ergo_user.id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n const sqlSelect = `SELECT ergo_property_spaces_images.*, ergo_spaces.category AS space_category, ergo_property.name AS property_name, ergo_photo.url AS photo_url, IF(default_image_id = photo_id, 1, 0) AS default_image, email, settings ${commonSql}`;\n\n await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-spaces-amenitites/:method', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Space Amenities"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query db to get property space amenities\n Used services: - ProjectMiddleware\n- UrlMiddleware\n- HostMiddleware\n The code with comments is:\n ```\n // Custom API for Property Space Amenities\n const tables = [\"property_spaces_amenitites\", \"property_spaces\", \"amenity\", \"property\", \"spaces\"];\n\n const commonSql = `FROM ergo_property_spaces_amenitites LEFT JOIN ergo_property_spaces ON ergo_property_spaces_amenitites.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_amenity ON ergo_property_spaces_amenitites.amenity_id = ergo_amenity.id LEFT JOIN ergo_property ON ergo_property_spaces.property_id = ergo_property.id LEFT JOIN ergo_spaces ON ergo_property_spaces.space_id = ergo_spaces.id LEFT JOIN ergo_user ON ergo_user.id = ergo_property.host_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n const sqlSelect = `SELECT ergo_property_spaces_amenitites.*, ergo_amenity.name AS amenity_name, ergo_property.name AS property_name, ergo_spaces.category AS space_category, email ${commonSql}`;\n\n return await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-addons/:method', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Add-Ons"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Returns addons of the property\n Used services: - ProjectMiddleware\n- UrlMiddleware\n- HostMiddleware\n The code with comments is:\n ```\n // Custom API for Property Add-Ons\n const tables = [\"property_add_on\", \"property\", \"add_on\"];\n\n const commonSql = `FROM ergo_property_add_on LEFT JOIN ergo_property ON ergo_property_add_on.property_id = ergo_property.id LEFT JOIN ergo_add_on ON ergo_property_add_on.add_on_id = ergo_add_on.id LEFT JOIN ergo_user ON ergo_user.id = ergo_property.host_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n const sqlSelect = `SELECT ergo_property_add_on.*, ergo_property.name AS property_name, ergo_add_on.name AS add_on_name, cost, email ${commonSql}`;\n\n await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/booking-addon', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Booking Add-Ons"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get booking addons from the db\n Used services: \n The code with comments is:\n ```\n // Custom API for Booking Add-Ons\n const tables = [\"booking_addons\", \"booking\", \"property_add_on\", \"add_on\", \"property\", \"property_spaces\", \"spaces\"];\n const joinFields = [\"booking_id\", \"id\", \"property_add_on_id\", \"add_on_id\", \"property_id\", \"space_id\", \"property_space_id\"];\n const renamedTables = tables.map((table) => `ergo_${table}`);\n\n const sqlCount = `SELECT COUNT(*) as num FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[0]} = ${renamedTables[1]}.${joinFields[1]} LEFT JOIN ${renamedTables[2]} ON ${renamedTables[0]}.${joinFields[2]} = ${renamedTables[2]}.${joinFields[1]} LEFT JOIN ${renamedTables[3]} ON ${renamedTables[2]}.${joinFields[3]} = ${renamedTables[3]}.${joinFields[1]} LEFT JOIN ${renamedTables[4]} ON ${renamedTables[2]}.${joinFields[4]} = ${renamedTables[4]}.${joinFields[1]} LEFT JOIN ${renamedTables[5]} ON ${renamedTables[1]}.${joinFields[6]} = ${renamedTables[5]}.${joinFields[1]} LEFT JOIN ${renamedTables[6]} ON ${renamedTables[5]}.${joinFields[5]} = ${renamedTables[6]}.${joinFields[1]} WHERE `;\n\n const sqlSelect = `SELECT ergo_booking_addons.*, ergo_add_on.name AS add_on_name, ergo_property.name AS property_name, ergo_spaces.category AS space_category FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[0]} = ${renamedTables[1]}.${joinFields[1]} LEFT JOIN ${renamedTables[2]} ON ${renamedTables[0]}.${joinFields[2]} = ${renamedTables[2]}.${joinFields[1]} LEFT JOIN ${renamedTables[3]} ON ${renamedTables[2]}.${joinFields[3]} = ${renamedTables[3]}.${joinFields[1]} LEFT JOIN ${renamedTables[4]} ON ${renamedTables[2]}.${joinFields[4]} = ${renamedTables[4]}.${joinFields[1]} LEFT JOIN ${renamedTables[5]} ON ${renamedTables[1]}.${joinFields[6]} = ${renamedTables[5]}.${joinFields[1]} LEFT JOIN ${renamedTables[6]} ON ${renamedTables[5]}.${joinFields[5]} = ${renamedTables[6]}.${joinFields[1]} WHERE `;\n\n await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.get('/v2/api/custom/ergo/google-auth', async function (req, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Google OAuth Invoked by Google"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Google OAuth endpoint Invoked by Google\n Used services: \n The code with comments is:\n ```\n // Google OAuth Invoked by Google\n const code = req.query.code;\n const options = {\n code,\n client_id: localConfig.GOOGLE_CLIENT_ID,\n client_secret: localConfig.GOOGLE_CLIENT_SECRET,\n redirect_uri: localConfig.GOOGLE_REDIRECT_URI,\n grant_type: localConfig.GOOGLE_GRANT_TYPE\n };\n\n const result = await fetch(localConfig.GOOGLE_TOKEN_URI, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\"\n },\n body: new URLSearchParams(options)\n });\n\n const data = await result.json();\n\n res.redirect(localConfig.OAUTH_SUCCESS_REDIRECT_URI + new URLSearchParams(data));\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/google-calendar-sync', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Google Calendar Sync\n- Send email notification 24 hours before the event\n- Show popup notification 10 minutes before the event\n"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Sync booking events to user's google calendar\n Used services: \n The code with comments is:\n ```\n // Google Calendar Sync\n const { access_token, expires_in, refresh_token, scope, token_type, summary, location, description, startTime, endTime, attendees } = req.body;\n\n async function createEvent(auth) {\n const calendar = google.calendar({ version: \"v3\", auth });\n\n const event = {\n summary,\n location,\n description,\n start: {\n dateTime: startTime,\n timeZone: \"UTC\"\n },\n end: {\n dateTime: endTime,\n timeZone: \"UTC\"\n },\n attendees: attendees.map((attendee) => ({ email: attendee })),\n reminders: {\n useDefault: false,\n overrides: [\n { method: \"email\", minutes: 24 * 60 }, // Remark: Send email notification 24 hours before the event\n { method: \"popup\", minutes: 10 } // Remark: Show popup notification 10 minutes before the event\n ]\n }\n };\n\n calendar.events.insert(\n {\n auth: auth,\n calendarId: \"primary\",\n resource: event\n },\n function (err, event) {\n if (err) {\n console.log(\"There was an error contacting the Calendar service: \" + err);\n return;\n }\n\n res.status(201).json({\n error: false,\n message: \"Event Created!\",\n eventLink: event.data.htmlLink\n });\n }\n );\n }\n\n const oauth2Client = new google.auth.OAuth2(localConfig.GOOGLE_CLIENT_ID, localConfig.GOOGLE_CLIENT_SECRET, localConfig.GOOGLE_REDIRECT_URI);\n oauth2Client.setCredentials({ access_token, expires_in, refresh_token, scope, token_type });\n\n await createEvent(oauth2Client);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/review/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Review"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Paginate, Create or Update review based on the associated method\n Used services: - sdk\n- sdk.get\n- sdk.insert\n\n The code with comments is:\n ```\n // Custom API for Review\n const { user } = req.body;\n\n let tables, joinFields, renamedTables, commonSql, sqlCount, sqlSelect;\n\n const sdk = req.sdk;\n sdk.getDatabase();\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"permission\");\n const permission = await sdk.get({\n role: req.role\n });\n\n const acl = JSON.parse(permission[0].permission);\n\n switch (req.params.method) {\n case \"PAGINATE\":\n tables = [\"review\", \"booking\", \"user\", \"property_spaces\", \"spaces\", \"property\"];\n joinFields = [\"booking_id\", \"id\", \"host_id\", \"customer_id\", \"property_spaces_id\", \"space_id\", \"property_id\"];\n renamedTables = tables.map((table) => `ergo_${table}`);\n\n commonSql = `FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[0]} = ${renamedTables[1]}.${joinFields[1]} LEFT JOIN ${renamedTables[2]} AS host ON host.${joinFields[1]} = ${renamedTables[0]}.${joinFields[2]} LEFT JOIN ${renamedTables[2]} AS customer ON customer.${joinFields[1]} = ${renamedTables[0]}.${joinFields[3]} LEFT JOIN ${renamedTables[3]} ON ${renamedTables[3]}.${joinFields[1]} = ${renamedTables[0]}.${joinFields[4]} LEFT JOIN ${renamedTables[4]} ON ${renamedTables[4]}.${joinFields[1]} = ${renamedTables[3]}.${joinFields[5]} LEFT JOIN ${renamedTables[5]} ON ${renamedTables[5]}.${joinFields[1]} = ${renamedTables[3]}.${joinFields[6]} WHERE `;\n\n sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n sqlSelect = `SELECT ergo_review.*, ergo_booking.booking_start_time, ergo_spaces.category, ergo_property.name, host.first_name AS host_first_name, host.last_name AS host_last_name, customer.first_name AS customer_first_name, customer.last_name AS customer_last_name, (CASE WHEN given_by = 'host' THEN customer_rating ELSE host_rating END) AS rating, (CASE WHEN given_by = '${user}' THEN 'given' ELSE 'received' END) AS type ${commonSql}`;\n\n return await handleReq(req, res, tables, sqlCount, sqlSelect);\n case \"get-hashtag\":\n tables = [\"review_hashtag\", \"hashtag\"];\n joinFields = [\"hashtag_id\", \"id\"];\n renamedTables = tables.map((table) => `ergo_${table}`);\n\n sqlSelect = `SELECT ergo_review_hashtag.*, ergo_hashtag.name FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[0]} = ${renamedTables[1]}.${joinFields[1]} WHERE `;\n\n req.params.method = \"RAW\";\n const result = await handleReq(req, res, tables, null, sqlSelect);\n\n return res.status(200).json({\n error: false,\n list: result\n });\n case \"POST\":\n if (existPermission(acl, \"review\", \"POST\")) {\n const payload = await preProcessReq(req, res, sdk, acl, \"review\", \"POST\");\n if (!payload) return;\n\n payload.post_date && (payload.post_date = sqlDateTimeFormat(new Date(payload.post_date)));\n\n const insertResult = await sdk.insert({\n ...payload,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n return res.status(200).json({\n error: false,\n message: insertResult\n });\n } else {\n return res.status(403).json({\n error: true,\n message: \"Invalid Resource\"\n });\n }\n case \"PUT\":\n if (existPermission(acl, \"review\", \"PUT\")) {\n const payload = await preProcessReq(req, res, sdk, acl, \"review\", \"PUT\");\n\n if (!payload) return;\n\n if (!req.body.id) {\n return res.status(403).json({\n error: true,\n message: \"ID Missing\",\n validation: { id: \"id missing\" }\n });\n }\n\n const id = req.body.id;\n delete payload.id;\n\n payload.post_date && (payload.post_date = sqlDateTimeFormat(new Date(payload.post_date)));\n\n await sdk.update(\n {\n ...payload,\n update_at: sqlDateTimeFormat(new Date())\n },\n id\n );\n return res.status(200).json({\n error: false,\n message: id\n });\n } else {\n return res.status(403).json({\n error: true,\n message: \"Invalid Resource\"\n });\n }\n default:\n res.status(403);\n res.json({\n error: true,\n message: \"Invalid Method\"\n });\n break;\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/scheduling/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Scheduling"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get property space's scheduling\n Used services: \n The code with comments is:\n ```\n // Custom API for Scheduling\n const tables = [\"property_spaces\", \"property\", \"spaces\", \"user\"];\n const joinFields = [\"host_id\", \"id\", \"property_id\", \"space_id\"];\n const renamedTables = tables.map((table) => `ergo_${table}`);\n\n const commonSql = `FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[2]} = ${renamedTables[1]}.${joinFields[1]} LEFT JOIN ${renamedTables[2]} ON ${renamedTables[2]}.${joinFields[1]} = ${renamedTables[0]}.${joinFields[3]} LEFT JOIN ${renamedTables[3]} ON ${renamedTables[3]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT ergo_property_spaces.*, ergo_user.first_name AS host_first_name, ergo_user.last_name AS host_last_name, ergo_property.name AS property_name, ergo_spaces.category AS space_category ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n return await handleReq(req, res, tables, sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-spaces-schedule-template/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Spaces Schedule Template"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get property space schedule template\n Used services: \n The code with comments is:\n ```\n // Custom API for Property Spaces Schedule Template\n const commonSql = `FROM ergo_property_spaces_schedule_template LEFT JOIN ergo_property_spaces ON ergo_property_spaces_schedule_template.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_schedule_template ON ergo_schedule_template.id = ergo_property_spaces_schedule_template.schedule_template_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT ergo_property_spaces_schedule_template.*, template_name, host_id ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/top-hosts', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get Top Rated Hosts"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Returns top rated hosts with pagination\n Used services: \n The code with comments is:\n ```\n // Get Top Rated Hosts\n const commonSql = `FROM ergo_user LEFT JOIN ergo_profile ON ergo_profile.user_id = ergo_user.id LEFT JOIN ergo_photo ON ergo_photo.id = ergo_user.photo LEFT JOIN ergo_property ON ergo_property.host_id = ergo_user.id LEFT JOIN ergo_property_spaces ON ergo_property_spaces.property_id = ergo_property.id LEFT JOIN ergo_spaces ON ergo_spaces.id = ergo_property_spaces.space_id WHERE role = \"host\" AND `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() AS num ${commonSql}`;\n\n const sqlSelect = `SELECT ergo_user.id, role, first_name, last_name, email, phone, photo, ergo_profile.address_line_1, ergo_profile.address_line_2, ergo_profile.city, ergo_profile.country, ergo_profile.zip, ROUND((SELECT AVG(IF(status = 1, host_rating, NULL)) FROM ergo_review WHERE host_id = ergo_user.id), 2) AS avg_host_rating, (SELECT SUM(status = 1) FROM ergo_review WHERE host_id = ergo_user.id AND host_rating IS NOT NULL) AS rating_count ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect, \" ergo_user.id \");\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/review-hashtag/:method', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get Reviews with Hashtags"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get reviews along with hashtags\n Used services: \n The code with comments is:\n ```\n // Get Reviews with Hashtags\n const { user } = req.body;\n\n switch (req.params.method) {\n case \"PAGINATE\":\n\n commonSql = `FROM ergo_review LEFT JOIN ergo_booking ON ergo_review.booking_id = ergo_booking.id LEFT JOIN ergo_user AS host ON host.id = ergo_review.host_id LEFT JOIN ergo_user AS customer ON customer.id = ergo_review.customer_id LEFT JOIN ergo_property_spaces ON ergo_property_spaces.id = ergo_review.property_spaces_id LEFT JOIN ergo_spaces ON ergo_spaces.id = ergo_property_spaces.space_id LEFT JOIN ergo_property ON ergo_property.id = ergo_property_spaces.property_id LEFT JOIN ergo_review_hashtag ON ergo_review_hashtag.review_id = ergo_review.id LEFT JOIN ergo_hashtag ON ergo_hashtag.id = ergo_review_hashtag.hashtag_id WHERE `;\n\n sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n sqlSelect = `SELECT ergo_review.*, ergo_booking.booking_start_time, ergo_spaces.category, ergo_property.name, host.first_name AS host_first_name, host.last_name AS host_last_name, host.is_photo_approved AS host_photo_approved, customer.first_name AS customer_first_name, customer.last_name AS customer_last_name, customer.is_photo_approved AS customer_photo_approved, (CASE WHEN given_by = 'host' THEN host.photo ELSE customer.photo END) AS photo, (CASE WHEN given_by = 'host' THEN customer_rating ELSE host_rating END) AS rating, (CASE WHEN given_by = '${user}' THEN 'given' ELSE 'received' END) AS type, GROUP_CONCAT(ergo_hashtag.name) AS hashtags ${commonSql}`;\n\n await handleReq(req, res, [], sqlCount, sqlSelect, \" ergo_review.id \");\n break;\n default:\n res.status(403);\n res.json({\n error: true,\n message: \"Invalid Method\"\n });\n break;\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/favourite/:method', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get Favourite Property Spaces"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get favourite property spaces\n Used services: \n The code with comments is:\n ```\n // Get Favourite Property Spaces\n switch (req.params.method) {\n case \"PAGINATE\":\n commonSql = `FROM ergo_property_spaces INNER JOIN ergo_user_property_spaces ON ergo_user_property_spaces.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_property ON ergo_property.id = ergo_property_spaces.property_id LEFT JOIN ergo_spaces ON ergo_spaces.id = ergo_property_spaces.space_id LEFT JOIN ergo_review ON ergo_review.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_photo ON ergo_photo.id = ergo_property_spaces.default_image_id WHERE `;\n\n sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n sqlSelect = `SELECT ergo_property_spaces.*, AVG(ergo_review.space_rating) AS average_space_rating, COUNT(ergo_review.space_rating) AS space_rating_count, ergo_property.name, category, ergo_property.address_line_1, ergo_property.address_line_2, ergo_property.city, ergo_property.country, ergo_property.zip, url ${commonSql}`;\n\n await handleReq(req, res, [], sqlCount, sqlSelect, \" ergo_property_spaces.id \");\n break;\n default:\n res.status(403);\n res.json({\n error: true,\n message: \"Invalid Method\"\n });\n break;\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/pay', [middlewares], ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Calculate the total amount to be charged\n- Charge Immediately if Customer has Saved Card\n- Update the booking Based on the Payment Status\n- Add Booking to Queue Table"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Calculate the total amount to be charged and chrage the user if has saved card and update booking status and payment status. If user doesn't have payment method then create payment intent and return it.\n Used services: - ValidationService.validateInputMethod\n- sdk.setProjectId\n- sdk.setProjectId\n- sdk.setTable\n- sdk.update\n- sdk.insert\n\n The code with comments is:\n ```\n const validationResult = await ValidationService.validateInputMethod(\n {\n booking_id: \"required|integer\",\n user_id: \"required|integer\",\n stripe_uid: \"string\",\n payment_method: \"string\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { booking_id, user_id, stripe_uid, payment_method } = req.body;\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n // Calculate the total amount to be charged\n const sqlSelect = `SELECT ergo_user.id AS customer_id, ((ergo_booking.booked_unit * ergo_property_spaces.rate * (ergo_booking.duration / 3600) + (ergo_booking.duration / 3600) * num_guests * additional_guest_rate) + ((ergo_booking.booked_unit * ergo_property_spaces.rate * (ergo_booking.duration / 3600) + (ergo_booking.duration / 3600) * num_guests * additional_guest_rate) * ergo_booking.tax_rate)/100) + (SELECT COALESCE(SUM(ergo_add_on.cost), 0) FROM ergo_booking_addons LEFT JOIN ergo_property_add_on ON ergo_property_add_on.id = ergo_booking_addons.property_add_on_id LEFT JOIN ergo_add_on ON ergo_add_on.id = ergo_property_add_on.add_on_id WHERE ergo_booking.id = ergo_booking_addons.booking_id) AS total, payment_method, stripe_uid FROM ergo_booking LEFT JOIN ergo_property_spaces ON ergo_booking.property_space_id = ergo_property_spaces.id LEFT JOIN ergo_property ON ergo_property_spaces.property_id = ergo_property.id LEFT JOIN ergo_spaces ON ergo_property_spaces.space_id = ergo_spaces.id LEFT JOIN ergo_user ON ergo_booking.customer_id = ergo_user.id WHERE ergo_booking.id=${booking_id} AND `;\n\n req.params.method = \"RAW\";\n const [result] = await handleReq(req, res, [], null, sqlSelect);\n if (!result) return res.status(400).json({ error: true, message: \"No booking found!\" });\n\n // Charge Immediately if Customer has Saved Card\n if (stripe_uid && payment_method) {\n const paymentIntent = await stripe.paymentIntents.create({\n payment_method_types: [\"card\"],\n amount: Math.round(result.total * 100),\n currency: \"usd\",\n metadata: {\n projectId: req.projectId,\n booking_id,\n user_id,\n immediate: true\n },\n customer: stripe_uid,\n payment_method: payment_method,\n off_session: true,\n confirm: true\n });\n\n // Update the booking Based on the Payment Status\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"booking\");\n if (paymentIntent.status !== \"succeeded\") {\n await sdk.update(\n {\n status: 4,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n\n return res.status(403).json({ error: true, message: \"Payment Failed! Booking Declined!\" });\n }\n\n if (paymentIntent.status === \"succeeded\") {\n sdk.setTable(\"stripe_payment_intent\");\n const stripePIResult = await sdk.insert({\n user_id,\n stripe_id: paymentIntent.id,\n object: JSON.stringify(paymentIntent),\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n sdk.setTable(\"booking\");\n await sdk.update(\n {\n status: 2,\n payment_status: 1,\n stripe_payment_intent_id: stripePIResult,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n\n // Add Booking to Queue Table\n sdk.setTable(\"booking_queue\");\n await sdk.insert({\n booking_id,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n return res.status(200).json({ success: true, message: \"Booked!\" });\n }\n }\n\n const paymentIntent = await stripe.paymentIntents.create({\n amount: Math.round(result.total * 100),\n currency: \"usd\",\n automatic_payment_methods: { enabled: true },\n metadata: {\n projectId: req.projectId,\n booking_id,\n user_id\n }\n });\n\n return res.status(200).json({\n error: false,\n message: \"Success\",\n payment_intent: paymentIntent\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/refund', [middlewares], ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Refund Paymment\n- Cancell Booking\n- remove booking queue"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get user's payment info and use it to refund. Then cancel the booking.\n Used services: - sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- ValidationService.validateInputMethod\n- sdk.update\n- sdk.deleteWhere\n The code with comments is:\n ```\n const validationResult = await ValidationService.validateInputMethod(\n {\n booking_id: \"required|integer\",\n stripe_payment_intent_id: \"required|integer\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { booking_id, stripe_payment_intent_id } = req.body;\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n sdk.setTable(\"stripe_payment_intent\");\n const [stripePI] = await sdk.get({ id: stripe_payment_intent_id });\n if (!stripePI) return res.status(400).json({ error: true, message: \"No payment info found.\" });\n\n // Refund Paymment\n const refund = await stripe.refunds.create({\n payment_intent: stripePI.stripe_id\n // amount: 1000\n });\n\n if (refund.status !== \"succeeded\") return res.status(400).json({ error: true, message: \"Failed to refund!\" });\n\n // Cancell Booking\n sdk.setTable(\"booking\");\n await sdk.update(\n {\n status: 5,\n payment_status: 4,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n\n // remove booking queue\n sdk.setTable(\"booking_queue\");\n await sdk.deleteWhere({ booking_id });\n\n return res.status(200).json({ error: false, message: \"Booking Cancelled and Refunded!\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/webhook', async (req, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - If Not Success Event Set payment_status to Failed\n- Make Booking Upcoming"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n This webhook listens for the events coming from stripe and updates payment status and booking\n Used services: - sdk.setDatabase\n- sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- sdk.update\n- sdk.insert\n The code with comments is:\n ```\n const event = req.body;\n\n const payment = event.data.object;\n const { projectId, booking_id, user_id, immediate } = payment.metadata;\n\n if (projectId !== \"ergo\") return res.status(200).json({ success: true, message: \"The event is captured but it's not for this endpoint!\" });\n\n if (immediate) return res.status(200).json({ success: true, message: \"Successfully Booked!\" });\n\n if (!projectId || !booking_id) return res.status(400).json({ success: false, message: \"Project/Booking ID is missing!\" });\n\n const sdk = new ManaKnightSDK();\n sdk.setDatabase(config.databaseName);\n sdk.setProjectId(projectId);\n sdk.setTable(\"stripe_webhook\");\n\n const webhookHandledBefore = await sdk.get({ is_handled: true, stripe_id: event.id, event_type: event.type });\n if (webhookHandledBefore[0]) res.status(200).json({ success: true, message: \"Webhook handled before\" });\n\n // If Not Success Event Set payment_status to Failed\n if (event.type !== \"payment_intent.succeeded\") {\n sdk.setTable(\"booking\");\n await sdk.update(\n {\n payment_status: 3,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n return res.status(403).json({ success: false, message: \"Payment Failed! Booking Declined!\" });\n }\n\n // Make Booking Upcoming(1)\n sdk.setTable(\"stripe_payment_intent\");\n const stripePIResult = await sdk.insert({\n user_id,\n stripe_id: payment.id,\n object: JSON.stringify(payment),\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n sdk.setTable(\"booking\");\n await sdk.update(\n {\n payment_status: 1,\n stripe_payment_intent_id: stripePIResult,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n\n // sdk.setTable(\"booking_queue\");\n // await sdk.insert({\n // booking_id,\n // create_at: sqlDateFormat(new Date()),\n // update_at: sqlDateTimeFormat(new Date())\n // });\n\n sdk.setTable(\"stripe_webhook\");\n await sdk.insert({\n stripe_id: event.id,\n description: event.data.object.object,\n event_type: event.type,\n resource_type: event.type,\n object: JSON.stringify(event.data.object),\n is_handled: true,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n return res.status(200).json({ success: true, message: \"Booked!\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.get('/v2/api/custom/ergo/spaces', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get All Spaces"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Return all spaces from db\n Used services: \n The code with comments is:\n ```\n // Get All Spaces\n if (req.body.where) req.body.where = [];\n const sqlSelect = `SELECT * from ergo_spaces WHERE `;\n\n req.params.method = \"RAW\";\n const result = await handleReq(req, res, [], null, sqlSelect);\n\n res.status(200).json({\n error: false,\n list: result\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v3/api/custom/ergo/device', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - If Password Reset Required Prevent Login\n- Check If User Is Active\n- Check If User Is Verified\n- Check If Already Logged In\n- Register Unknown Device\n- Update last_login_time"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate input then check if password reset required, user active and verified and already logged in. Store the unknown device and update last login time\n Used services: - sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- sdk.update\n- sdk.insert\n- ValidationService.validateInputMethod\n The code with comments is:\n ```\n const validationResult = await ValidationService.validateInputMethod(\n {\n uid: \"required\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { uid } = req.body;\n\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"user\");\n\n const [userInfo] = await sdk.get({ id: req.user_id });\n if (!userInfo || userInfo.deleted_at)\n return res.status(403).json({\n error: true,\n message: \"This email is not registered!\"\n });\n const { id, reset_pass } = userInfo;\n\n // If Password Reset Required Prevent Login\n if (reset_pass)\n return res.status(401).json({\n error: true,\n message: \"Please reset your password before trying to login.\"\n });\n\n // Check If User Is Active\n if (!userInfo.status)\n return res.status(403).json({\n error: true,\n message: \"Your account is inactive\"\n });\n\n // Check If User Is Verified\n if (!userInfo.verify)\n return res.status(403).json({\n error: true,\n message: \"Your account is not verified\"\n });\n\n // Check If Already Logged In\n sdk.setTable(\"device\");\n const [loggedIn] = await sdk.get({ user_id: id, active: 1 });\n if (loggedIn && loggedIn.uid !== uid) sdk.updateWhere({ active: 0 }, { user_id: id, active: 1 });\n\n // Register Unknown Device\n const [deviceInfo] = await sdk.get({ user_id: id, uid });\n\n if (!deviceInfo) {\n await sdk.insert({\n user_id: id,\n uid,\n last_login_time: sqlDateTimeFormat(new Date()),\n create_at: sqlDateTimeFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n } else {\n // Update last_login_time\n await sdk.update(\n {\n last_login_time: sqlDateTimeFormat(new Date()),\n active: 1,\n update_at: sqlDateTimeFormat(new Date())\n },\n deviceInfo.id\n );\n }\n\n return res.status(200).json({ error: false, message: \"Success\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v3/api/ergo/logout', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n "}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n It logs out user from a known device\n Used services: - ValidationService.validateInputMethod\n- sdk.setProjectId\n- sdk.setTable\n- sdk.updateWhere\n The code with comments is:\n ```\n const validationResult = await ValidationService.validateInputMethod(\n {\n uid: \"required\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { uid } = req.body;\n\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"device\");\n\n await sdk.updateWhere(\n {\n active: 0,\n update_at: sqlDateTimeFormat(new Date())\n },\n { user_id: req.user_id, uid }\n );\n\n return res.status(200).json({\n error: false,\n message: \"Successfully logged out\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/id-verification', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n "}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get id verification info\n Used services: \n The code with comments is:\n ```\n commonSql = `FROM ergo_id_verification LEFT JOIN ergo_user ON ergo_user.id = ergo_id_verification.user_id LEFT JOIN ergo_profile ON ergo_profile.user_id = ergo_user.id WHERE `;\n\n sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n sqlSelect = `SELECT ergo_id_verification.*, first_name, last_name, ergo_user.create_at AS user_create_at, role, ergo_user.status AS user_status, email, dob ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/custom/ergo/confirm-delete-email', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Send Delete Account Email"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Send a confirmation email to user after deleting account\n Used services: - ValidationService.validateInputMethod\n- JwtService.createAccessToken\n- sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- MailService.initialize\n- MailService.send\n The code with comments is:\n ```\n // Send Delete Account Email\n const validationResult = await ValidationService.validateInputMethod(\n {\n email: \"required|email\",\n user_id: \"required|integer\",\n role: \"required|string\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { email, user_id, role } = req.body;\n\n const token = JwtService.createAccessToken(\n {\n user_id,\n role\n },\n config.jwt_expire,\n config.jwt_key\n );\n\n const sdk = req.sdk;\n\n let project;\n if (config.env == \"production\") {\n project = require(\"../../project\");\n } else {\n sdk.setProjectId(\"manaknight\");\n sdk.setTable(\"projects\");\n [project] = await sdk.get({\n project_id: req.projectId\n });\n }\n\n if (!project) return res.status(400).json({ error: true, message: \"Project not found\" });\n\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"email\");\n const [template] = await sdk.get({\n slug: \"delete-account\"\n });\n if (!template) return res.status(400).json({ error: true, message: \"Template not found\" });\n\n MailService.initialize(config);\n const { subject, html } = MailService.inject(\n { body: template.html, subject: template.subject },\n { link: `Link` }\n );\n\n const mailResult = await MailService.send(config.mail_user, email, subject, html);\n\n if (typeof mailResult === \"string\") {\n return res.status(403).json({\n error: true,\n message: mailResult\n });\n }\n if (mailResult.error) return res.status(403).json({ error: true, mailResult });\n\n return res.status(200).json({\n error: false,\n message: \"Confirmation email has been sent!\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/custom/ergo/reset', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Reset Password"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate input, check code compare hash and update password in db\n Used services: - ValidationService.validateInputMethod\n- ProjectMiddleware\n- UrlMiddleware\n- HostMiddleware\n- PasswordService.compareHash\n- AuthService\n- sdk.get\n- sdk.updateWhere\n The code with comments is:\n ```\n // Reset Password\n const validationResult = await ValidationService.validateInputMethod(\n {\n code: \"required|integer\",\n token: \"required|string\",\n password: \"required|string\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n let sdk = req.sdk;\n sdk.getDatabase();\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"token\");\n\n const [result] = await sdk.get({ token: req.body.token });\n if (!result)\n return res.status(403).json({\n error: true,\n message: \"Invalid Reset Request\"\n });\n\n const data = JSON.parse(result.data);\n const code = data.code;\n\n if (req.body.code != code)\n return res.status(403).json({\n error: true,\n message: \"Mismatch Reset Code\",\n validation: [{ field: \"code\", message: \"Mismatch Reset Code\" }]\n });\n\n sdk.setTable(\"user\");\n const [user] = await sdk.get({ id: result.user_id });\n\n if (!user)\n return res.status(403).json({\n error: true,\n message: \"User not found\"\n });\n\n const same = await PasswordService.compareHash(req.body.password, user.password);\n if (same)\n return res.status(403).json({\n error: true,\n message: \"Password is same as old password\",\n validation: [{ field: \"password\", message: \"Password is same as old password\" }]\n });\n\n const service = new AuthService();\n const resetResult = await service.reset(sdk, req.projectId, result, req.body.password);\n\n if (typeof resetResult == \"string\")\n return res.status(403).json({\n error: true,\n message: resetResult\n });\n\n sdk.setTable(\"user\");\n await sdk.updateWhere(\n {\n reset_pass: 0,\n update_at: sqlDateTimeFormat(new Date())\n },\n { id: result.user_id }\n );\n\n return res.status(200).json({\n error: false,\n message: \"Reset Password\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.get('/v2/api/custom/ergo/s3proxy/:image', async function (req, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - S3 Proxy"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n A proxy wrapper for accessing s3 server.\n Used services: \n The code with comments is:\n ```\n // S3 Proxy\n const { image } = req.params;\n if (!image) return res.status(400).json({ error: true, message: \"image is required!\" });\n\n s3BaseUrl = config.s3_base_url || \"https://s3.us-west-1.amazonaws.com/com.mkdlab.images/baas/ergo\";\n\n const response = await fetch(`${s3BaseUrl}/${image}`);\n const data = await response.arrayBuffer();\n\n res.contentType(\"image/jpeg\");\n res.send(Buffer.from(data));\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v3/api/ergo/slot-available', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Check If Slot Available For The Given Day\n- Fetching Max Unit of a Property Space and Check Availability\n- Fetch All Slots For The Property Space\n- Fetch Already Booked Bookings For The Given Date\n- Check Custom/Regular Slots\n- Find Available Slots\n- Reject booking if host has no active regular time slots for the booking date\n- Find Available Slots"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Check if property space available. Fetch slots of the property space. Check if there are any booked bookings. Look if slot is custom and find available slots for that custom timeframe. And if regular slot check if any scheduling for the day and find available slots withing the timeframe.\n Used services: \n The code with comments is:\n ```\n // Check If Slot Available For The Given Day\n const validationResult = await ValidationService.validateInputMethod(\n {\n date: \"required|date\",\n property_space_id: \"required|integer\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { date, property_space_id } = req.body;\n\n // Fetching Max Unit of a Property Space and Check Availability\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"property_spaces\");\n const [propertySpace] = await sdk.get({\n id: property_space_id,\n availability: 1\n });\n\n if (!propertySpace)\n return res.status(403).json({\n error: true,\n message: \"Property space is not available!\"\n });\n\n // Fetch All Slots For The Property Space\n const [slots] = await sdk.rawQuery(\n `SELECT * FROM ergo_property_spaces_schedule_template LEFT JOIN ergo_schedule_template ON ergo_schedule_template.id = ergo_property_spaces_schedule_template.schedule_template_id WHERE property_spaces_id = ${property_space_id}`\n );\n\n const parsedCustomSlots = JSON.parse(slots.custom_slots);\n const parsedRegularSlots = JSON.parse(slots.slots);\n const dateFormatted = new Intl.DateTimeFormat(\"en-US\", {\n year: \"2-digit\",\n month: \"2-digit\",\n day: \"2-digit\"\n }).format(new Date(date));\n const dateISO = new Date(date).toISOString();\n\n if (!slots || (!parsedRegularSlots.length && !Object.keys(parsedCustomSlots).length))\n return res.status(400).json({ error: true, message: \"No slots for this property space!\" });\n\n // Fetch Already Booked Bookings For The Given Date\n const bookedBookings = await sdk.rawQuery(\n `SELECT * FROM ergo_booking WHERE (DATE(booking_start_time) = '${date}' OR DATE(booking_end_time) = '${date}') AND property_space_id = ${property_space_id} AND status IN (1, 2)`\n );\n\n // Check Custom/Regular Slots\n if (parsedCustomSlots[dateFormatted]) {\n // Find Available Slots\n const ranges = parsedCustomSlots[dateFormatted];\n ranges.forEach((range) => {\n range.start = `${dateISO.split(\"T\")[0]}T${range.start.split(\"T\")[1]}`;\n range.end = `${dateISO.split(\"T\")[0]}T${range.end.split(\"T\")[1]}`;\n });\n\n const hourlySlots = getTimeSlots(ranges, 60);\n const availableSlots = getAvailableTimeSlots(hourlySlots, bookedBookings);\n\n if (!availableSlots.length)\n return res.status(400).json({\n error: true,\n message: \"All slots are occupaid!\",\n allOccupied: true\n });\n } else {\n // Reject booking if host has no active regular time slots for the booking date\n const bookingDay = new Date(date).toDateString().slice(0, 3).toLowerCase();\n if (!slots[bookingDay]) return res.status(400).json({ error: true, message: \"No scheduling for this day.\" });\n\n // Find Available Slots\n const ranges = parsedRegularSlots;\n ranges.forEach((range) => {\n range.start = `${dateISO.split(\"T\")[0]}T${range.start.split(\"T\")[1]}`;\n range.end = `${dateISO.split(\"T\")[0]}T${range.end.split(\"T\")[1]}`;\n });\n\n const hourlySlots = getTimeSlots(ranges, 60);\n const availableSlots = getAvailableTimeSlots(hourlySlots, bookedBookings);\n\n if (!availableSlots.length)\n return res.status(400).json({\n error: true,\n message: \"All slots are occupaid.\",\n allOccupied: true\n });\n }\n\n return res.status(200).json({\n error: false,\n message: \"Slots available\",\n allOccupied: false\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/admin/register', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Validation\n- Register user\n- Create refresh token if asked"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate the input data. Then register the user with customRegister method. Generate the token and return the response\n Used services: - logService.log\n- JwtService.createAccessToken\n- sdk\n The code with comments is:\n ```\n const needRefreshToken = req.body.is_refresh ? true : false;\n let refreshToken = undefined;\n\n // Validation\n if (req.role !== \"admin\")\n return res.status(403).json({\n error: true,\n message: \"You are not authorized\"\n // validation: [{ field: \"\", message: \"You are not authorized\" }]\n });\n\n if (!req.body.email) {\n return res.status(403).json({\n error: true,\n message: \"Email Missing\",\n validation: [{ field: \"email\", message: \"Email missing\" }]\n });\n }\n if (!req.body.role) {\n return res.status(403).json({\n error: true,\n message: \"Role Missing\",\n validation: [{ field: \"role\", message: \"role missing\" }]\n });\n }\n if (req.body.status === undefined) {\n return res.status(403).json({\n error: true,\n message: \"Status Missing\",\n validation: [{ field: \"status\", message: \"status missing\" }]\n });\n }\n if (!req.body.password) {\n return res.status(403).json({\n error: true,\n message: \"Password Missing\",\n validation: [{ field: \"password\", message: \"Password missing\" }]\n });\n }\n\n logService.log(req.projectId, req.body);\n\n // Register user\n const result = await customRegister(req.sdk, req.projectId, req.body);\n\n if (typeof result == \"string\") {\n return res.status(403).json({\n error: true,\n message: result\n });\n } else {\n // create refresh token if asked\n if (needRefreshToken) {\n const refreshToken = JwtService.createAccessToken(\n {\n user_id: result.id,\n role: req.body.role\n },\n config.refresh_jwt_expire,\n config.jwt_key\n );\n let expireDate = new Date();\n expireDate.setSeconds(expireDate.getSeconds() + config.refresh_jwt_expire);\n await service.saveRefreshToken(app.get(\"sdk\"), req.projectId, result.id, refreshToken, expireDate);\n }\n\n return res.status(200).json({\n error: false,\n role: req.body.role,\n token: JwtService.createAccessToken(\n {\n user_id: result,\n role: req.body.role\n },\n config.jwt_expire,\n config.jwt_key\n ),\n refresh_token: refreshToken,\n expire_at: config.jwt_expire,\n user_id: result\n });\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/admin/register/:user/:lawfirmId', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - validate user data\n- register user\n- store in db"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate user data, register user and store the infor in user\n Used services: - sdk\n- sdk.setProjectId\n- sdk.insert\n The code with comments is:\n ```\n // validate user data\n if ((req.params.user !== \"client\" && req.params.user !== \"attorney\") || req.params.user !== req.body.role)\n return res.status(403).json({\n error: true,\n message: \"Invalid User\"\n });\n\n if (req.body.role === \"admin\" || req.body.role === \"lawfirm\")\n return res.status(403).json({\n error: true,\n message: `${req.body.role} cannot be registered`\n });\n\n if (!req.body.email) {\n return res.status(403).json({\n error: true,\n message: \"Email Missing\",\n validation: [{ field: \"email\", message: \"Email missing\" }]\n });\n }\n if (!req.body.role) {\n return res.status(403).json({\n error: true,\n message: \"Role Missing\",\n validation: [{ field: \"role\", message: \"role missing\" }]\n });\n }\n if (req.body.status === undefined) {\n return res.status(403).json({\n error: true,\n message: \"Status Missing\",\n validation: [{ field: \"status\", message: \"status missing\" }]\n });\n }\n if (!req.body.password) {\n return res.status(403).json({\n error: true,\n message: \"Password Missing\",\n validation: [{ field: \"password\", message: \"Password missing\" }]\n });\n }\n\n // register user\n const result = await customRegister(req.sdk, req.projectId, req.body);\n\n if (typeof result === \"string\") {\n return res.status(403).json({\n error: true,\n message: result\n });\n }\n\n // store in db\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n if (req.params.user === \"client\") {\n sdk.setTable(\"law_firm_client\");\n await sdk.insert({\n law_firm_id: req.params.lawfirmId,\n client_id: result,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n }\n\n if (req.params.user === \"attorney\") {\n sdk.setTable(\"law_firm_attorney\");\n await sdk.insert({\n law_firm_id: req.params.lawfirmId,\n attorney_id: result,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n }\n\n return res.status(201).json({\n error: false,\n message: result\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/admin/get-user', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - validation\n- get users"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Vaidate input, fetch users form db and return response\n Used services: - logService.log\n- sdk\n The code with comments is:\n ```\n // validation\n if (!req.body.id) {\n return res.status(403).json({\n error: true,\n message: \"ID is Missing\",\n validation: [{ field: \"id\", message: \"ID is Missing\" }]\n });\n }\n\n logService.log(req.projectId, req.body.id);\n\n // get users\n const result = await customGet(req.sdk, req.projectId, req.body.id);\n\n if (Object.keys(result).length === 0)\n return res.status(404).json({\n error: true,\n message: \"Not Found\"\n });\n\n return res.status(200).json({\n error: false,\n ...result\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/lawfirm/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Fetch lawfirms"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Apply the query to db to get lawfirms with pagination.\n Used services: \n The code with comments is:\n ```\n // Fetch lawfirms\n const commonSql = `FROM forkfirm_user LEFT JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.law_firm_id = forkfirm_user.id LEFT JOIN forkfirm_profile ON forkfirm_user.id = forkfirm_profile.user_id WHERE forkfirm_user.role = \"lawfirm\" AND `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_user.*, forkfirm_profile.website AS website, forkfirm_profile.base_office AS base_office, (SELECT COUNT(forkfirm_law_firm_attorney.law_firm_id)) AS attorneys ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n req.body.groupBy = \" forkfirm_user.id \";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/client/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - get clients\n- get doc rates\n- get subclients\n- get law_firm"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Baed on the url param, \"method\" apply query for fetching clients or docs rates or subclients or lawfirm with pagination.\n Used services: \n The code with comments is:\n ```\n const commonSql = `FROM forkfirm_user LEFT JOIN forkfirm_profile ON forkfirm_user.id = forkfirm_profile.user_id LEFT JOIN forkfirm_client_tag ON forkfirm_client_tag.client_id = forkfirm_user.id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_client_tag.tag_id LEFT JOIN forkfirm_photo ON forkfirm_photo.id = forkfirm_profile.playbook WHERE forkfirm_user.role = 'client' AND `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_user.*, invited_by, forkfirm_profile.id AS profile_id, forkfirm_tag.name AS tag_name, url ${commonSql}`;\n\n // get clients\n if (req.params.method === \"PAGINATE\") {\n req.body.groupBy = \" forkfirm_user.id \";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get doc rates\n if (req.params.method === \"view-rates\") {\n const commonSql = `FROM forkfirm_user JOIN forkfirm_doc_type LEFT JOIN forkfirm_client_rate ON forkfirm_client_rate.doc_type_id = forkfirm_doc_type.id AND forkfirm_client_rate.client_id = forkfirm_user.id WHERE forkfirm_user.role = \"client\" AND `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_user.*, forkfirm_client_rate.id AS client_rate_id, rate, name AS doc_name ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get subclients\n if (req.params.method === \"subclient\") {\n const commonSql = `FROM forkfirm_user AS subclient LEFT JOIN forkfirm_client_subclient ON forkfirm_client_subclient.subclient_id = subclient.id LEFT JOIN forkfirm_user AS client ON client.id = forkfirm_client_subclient.client_id LEFT JOIN forkfirm_law_firm_client ON forkfirm_law_firm_client.client_id = client.id WHERE subclient.role = 'subclient' AND `;\n\n const sqlCount = `SELECT COUNT(*) OVER() AS num ${commonSql}`;\n\n const sqlSelect = `SELECT subclient.*, client.id AS client_id, client.first_name AS client_first_name, client.last_name AS client_last_name, client.email AS client_email, forkfirm_client_subclient.id AS forkfirm_client_subclient_id, show_billing, law_firm_id, JSON_ARRAYAGG(law_firm_id) AS all_lawfirms ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get law_firm\n if (req.params.method === \"view-law-firms\") {\n const tables = [\"user\", \"law_firm_client\"];\n\n const commonSql = `FROM forkfirm_user LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = forkfirm_user.id LEFT JOIN forkfirm_photo ON forkfirm_photo.id = forkfirm_profile.playbook LEFT JOIN forkfirm_law_firm_client ON forkfirm_user.id = forkfirm_law_firm_client.client_id LEFT JOIN forkfirm_user AS law_firm ON law_firm.id = forkfirm_law_firm_client.law_firm_id WHERE forkfirm_user.role = \"client\" AND `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_user.*, law_firm.first_name AS law_firm_first_name, law_firm.last_name AS law_firm_last_name, law_firm.id AS current_law_firm_id, forkfirm_law_firm_client.id AS law_firm_client_id, url AS playbook_url, invited_by ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/invoice/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - fetch invoices\n- export invoices"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n If the method is export, export the invoice. If paginate then get invoices with pagination.\n Used services: \n The code with comments is:\n ```\n const tables = [\"invoice\", \"user\"];\n const joinFields = [\"id\", \"client_id\"];\n const renamedTables = tables.map((table) => `forkfirm_${table}`);\n\n const sqlCount = `SELECT COUNT(*) as num FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n const sqlSelect = `SELECT forkfirm_invoice.*, forkfirm_user.email, forkfirm_user.first_name, forkfirm_user.last_name, CASE WHEN forkfirm_invoice.status = 0 THEN 'unpaid' WHEN forkfirm_invoice.status = 1 THEN 'paid' ELSE 'void' END AS status_string FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n // fetch invoices\n if (req.params.method === \"PAGINATE\") {\n const { date } = req.body;\n date ? req.body.where.push(`DATE_FORMAT(forkfirm_invoice.generated_at, '%Y-%m') = DATE_FORMAT('${date}', '%Y-%m')`) : \"\";\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // export invoices\n if (req.params.method === \"EXPORT\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/lawfirm-invoice/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - get lawfirm invoices\n- export lawfirm invoices"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n If the method is export, export the lawfirm invoice. If paginate then get lawfirm invoices with pagination.\n Used services: \n The code with comments is:\n ```\n const tables = [\"invoice_lawfirm\", \"user\"];\n const joinFields = [\"id\", \"lawfirm_id\"];\n const renamedTables = tables.map((table) => `forkfirm_${table}`);\n\n const sqlCount = `SELECT COUNT(*) as num FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n let sqlSelect = `SELECT forkfirm_invoice_lawfirm.*, email, first_name, last_name, CASE WHEN forkfirm_invoice_lawfirm.status = 0 THEN 'unpaid' WHEN forkfirm_invoice_lawfirm.status = 1 THEN 'paid' ELSE 'void' END AS status_string FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n // get lawfirm invoices\n if (req.params.method === \"PAGINATE\") {\n const { date } = req.body;\n date ? req.body.where.push(`DATE_FORMAT(forkfirm_invoice_lawfirm.generated_at, '%Y-%m') = DATE_FORMAT('${date}', '%Y-%m')`) : \"\";\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // export lawfirm invoices\n if (req.params.method === \"EXPORT\") {\n sqlSelect = `SELECT forkfirm_invoice_lawfirm.id, generated_at, lawfirm_id, email AS lawfirm_email, first_name AS lawfirm_name, charged_amount, fork_fee, payout_amount, CASE WHEN forkfirm_invoice_lawfirm.status = 0 THEN 'Unpaid' WHEN forkfirm_invoice_lawfirm.status = 1 THEN 'Paid' ELSE 'Void' END AS status, paid_at FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/contract/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - get contracts\n- get contract tags\n- get contract's docs"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n If method is paginate then it returns list of paginated contracts. If tag it returns contract's tags and for doc returns contract's docs. If export exports the contracts.\n Used services: \n The code with comments is:\n ```\n // get contracts\n req.body.groupBy = \"forkfirm_contract.id\";\n const commonSql = `FROM forkfirm_contract LEFT JOIN forkfirm_user AS client ON client.id = forkfirm_contract.client_id LEFT JOIN forkfirm_user AS attorney ON attorney.id = forkfirm_contract.attorney_id LEFT JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.attorney_id = attorney.id LEFT JOIN forkfirm_law_firm_client ON forkfirm_law_firm_client.client_id = client.id AND forkfirm_law_firm_attorney.law_firm_id = forkfirm_law_firm_client.law_firm_id LEFT JOIN forkfirm_user AS lawfirm ON lawfirm.id = forkfirm_law_firm_client.law_firm_id LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = lawfirm.id LEFT JOIN forkfirm_client_subclient ON forkfirm_client_subclient.client_id = client.id AND forkfirm_client_subclient.subclient_id = forkfirm_contract.originator LEFT JOIN forkfirm_user AS subclient ON subclient.id = forkfirm_client_subclient.subclient_id LEFT JOIN forkfirm_doc_type ON forkfirm_doc_type.id = forkfirm_contract.doc_type_id LEFT JOIN forkfirm_client_rate ON forkfirm_client_rate.doc_type_id = forkfirm_doc_type.id AND forkfirm_client_rate.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_client_hours ON forkfirm_client_hours.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_contract_tag ON forkfirm_contract_tag.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_contract_tag.tag_id LEFT JOIN forkfirm_client_tag ON forkfirm_client_tag.client_id = client.id LEFT JOIN forkfirm_tag AS client_tag ON client_tag.id = forkfirm_client_tag.tag_id WHERE `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract.*, lawfirm.first_name AS lawfirm_first_name, lawfirm.id AS lawfirm_id, lawfirm.last_name AS lawfirm_last_name, attorney.first_name AS attorney_first_name, attorney.last_name AS attorney_last_name, base_office AS lawfirm_office, client.first_name AS client_first_name, client.last_name AS client_last_name, client.email, hour, forkfirm_doc_type.name as doc_name, rate, IF(contract_type, rate * hour, rate) AS charged_amount, GROUP_CONCAT(DISTINCT CONCAT(client_tag.name, '|', client_tag.id, '|', client_tag.input_type, '|', client_tag.input_duration)) AS client_tag, GROUP_CONCAT(DISTINCT CONCAT(forkfirm_tag.name, '|', forkfirm_tag.id, '|', forkfirm_contract_tag.input_type, '|', COALESCE(forkfirm_contract_tag.input_duration,'null'))) AS contract_tag, subclient.id AS subclient_id ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get contract tags\n if (req.params.method === \"tag\") {\n req.params.method = \"PAGINATE\";\n req.body.groupBy = null;\n const commonSql = `FROM forkfirm_contract_tag LEFT JOIN forkfirm_contract ON forkfirm_contract.id = forkfirm_contract_tag.contract_id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_contract_tag.tag_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract_tag.*, name ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n // get contract's docs\n if (req.params.method === \"doc\") {\n req.params.method = \"PAGINATE\";\n req.body.groupBy = null;\n const commonSql = `FROM forkfirm_contract LEFT JOIN forkfirm_photo AS doc ON doc.id = forkfirm_contract.document_id LEFT JOIN forkfirm_photo AS doc2 ON doc2.id = forkfirm_contract.document_id2 LEFT JOIN forkfirm_photo AS doc3 ON doc3.id = forkfirm_contract.document_id3 LEFT JOIN forkfirm_photo AS executed_doc ON executed_doc.id = forkfirm_contract.executed_document_id LEFT JOIN forkfirm_photo AS executed_doc2 ON executed_doc2.id = forkfirm_contract.executed_document_id2 LEFT JOIN forkfirm_photo AS executed_doc3 ON executed_doc3.id = forkfirm_contract.executed_document_id3 WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract.id, doc.url AS doc_url, doc2.url AS doc2_url, doc3.url AS doc3_url, executed_doc.url AS executed_doc_url, executed_doc2.url AS executed_doc2_url, executed_doc3.url AS executed_doc3_url ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n if (req.params.method === \"EXPORT\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n return res.status(403).json({\n error: true,\n message: \"Invalid method\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/pending-contract/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - get pending contracts"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db for all pending contracts and returns them.\n Used services: \n The code with comments is:\n ```\n // get pending contracts\n req.body.groupBy = \"forkfirm_contract.id\";\n const commonSql = `FROM forkfirm_contract LEFT JOIN forkfirm_user AS client ON client.id = forkfirm_contract.client_id LEFT JOIN forkfirm_law_firm_client ON forkfirm_law_firm_client.client_id = client.id LEFT JOIN forkfirm_user AS attorney ON attorney.id = forkfirm_contract.attorney_id LEFT JOIN forkfirm_attorney_client ON forkfirm_attorney_client.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.attorney_id = forkfirm_contract.attorney_id LEFT JOIN forkfirm_user AS lawfirm ON lawfirm.id = forkfirm_law_firm_attorney.law_firm_id LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = forkfirm_law_firm_attorney.law_firm_id LEFT JOIN forkfirm_client_subclient ON forkfirm_client_subclient.client_id = forkfirm_contract.client_id AND forkfirm_client_subclient.subclient_id = forkfirm_contract.originator LEFT JOIN forkfirm_user AS subclient ON subclient.id = forkfirm_client_subclient.subclient_id LEFT JOIN forkfirm_doc_type ON forkfirm_doc_type.id = forkfirm_contract.doc_type_id LEFT JOIN forkfirm_client_rate ON forkfirm_client_rate.doc_type_id = forkfirm_doc_type.id AND forkfirm_client_rate.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_client_hours ON forkfirm_client_hours.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_contract_tag ON forkfirm_contract_tag.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_contract_tag.tag_id LEFT JOIN forkfirm_client_tag ON forkfirm_client_tag.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_tag AS client_tag ON client_tag.id = forkfirm_client_tag.tag_id WHERE `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract.*, lawfirm.first_name AS lawfirm_first_name, lawfirm.id AS lawfirm_id, lawfirm.last_name AS lawfirm_last_name, attorney.first_name AS attorney_first_name, attorney.last_name AS attorney_last_name, base_office AS lawfirm_office, client.first_name AS client_first_name, client.last_name AS client_last_name, client.email, hour, forkfirm_doc_type.name as doc_name, rate, IF(contract_type, rate * hour, rate) AS charged_amount, GROUP_CONCAT(DISTINCT CONCAT(client_tag.name, '|', client_tag.id, '|', client_tag.input_type, '|', client_tag.input_duration)) AS client_tag, subclient.id AS subclient_id ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n\n return res.status(403).json({\n error: true,\n message: \"Invalid method\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/forkfirm/admin/tag', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - validate data\n- get client's tags"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate input data and fetch client's tags from db and return data\n Used services: \n The code with comments is:\n ```\n const { where, page, limit, sortId, direction } = req.body;\n\n // validate data\n if ((page && typeof page !== \"number\") || (limit && typeof limit !== \"number\")) return res.status(400).json({ error: true, message: \"Invalid Request\" });\n\n if (sortId)\n req.body.sortId = sortId\n .split(\".\")\n .map((item) => `\\`${item}\\``)\n .join(\".\");\n\n if (direction) {\n if (direction === \"DESC\") req.body.direction = \"DESC\";\n else req.body.direction = \"ASC\";\n }\n\n if (where) {\n const searchStr = getSearchStr(where);\n req.body.where = searchStr;\n }\n\n const commonSql = `FROM forkfirm_client_tag LEFT JOIN forkfirm_user ON forkfirm_user.id = forkfirm_client_tag.client_id LEFT JOIN forkfirm_tag ON forkfirm_tag.id = forkfirm_client_tag.tag_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n // get client's tags\n const sqlSelect = `SELECT forkfirm_client_tag.*, first_name, last_name, forkfirm_tag.name AS tag_name, input_type, input_duration ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n acenanny_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_articles\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nthumbnail | varchar(255)\ntitle | varchar(255)\ncategory | int(11)\ntags | varchar(255)\ndescription | mediumtext\ncontent | mediumtext\n\nacenanny_background_check\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ntype | int(11)\nverification_id | varchar(255)\nstatus | varchar(255)\noutcome | varchar(255)\napplication_adjudication | varchar(255)\nreports | mediumtext\nverified_data | longtext\ndate | date\nlast_synced | datetime\n\nacenanny_blog_category\n----------------------\nName | Type\n\nid | int(11)\nparent_id | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_post\n------------------\nName | Type\n\nid | int(11)\ntitle | varchar(255)\ndescription | mediumtext\ncontent | longtext\nthumbnail | mediumtext\nauthor | int(11)\nmeta | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_post_category\n---------------------------\nName | Type\n\nid | int(11)\ncategory_id | int(11)\npost_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_post_tags\n-----------------------\nName | Type\n\nid | int(11)\ntag_id | int(11)\npost_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_post_views\n------------------------\nName | Type\n\nid | int(11)\npost_id | int(11)\nviewer_ip | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_blog_tags\n------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_favourites\n-------------------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nnanny_id | int(11)\n\nacenanny_nanny_profile\n----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nfirst_name | mediumtext\nlast_name | mediumtext\nfirst_name_other_lang | mediumtext\nlast_name_other_lang | mediumtext\nweChat | mediumtext\nlanguages | mediumtext\ncountry | mediumtext\ncity | mediumtext\nregion | mediumtext\nbirthyear | mediumtext\nschool | mediumtext\ndegree | mediumtext\nstudy_field | mediumtext\nintro | longtext\nprofile_picture | mediumtext\nadditional_pictures | longtext\ninterview | longtext\ncertificates | longtext\nattribute_details | mediumtext\nnanny_type | mediumtext\nlocation_type | mediumtext\nlocations | mediumtext\nemployment_type | mediumtext\nstart_date | mediumtext\nschedule | longtext\nexperience_and_skills | longtext\nidentity_verification_id | longtext\nstatus | int(11)\naccount_status | int(11)\nsignup_stage | int(11)\nsubmitted | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_nanny_reviews\n----------------------\nName | Type\n\nid | int(11)\nnanny_id | int(11)\nuser_id | int(11)\nrating | int(11)\nflag | int(11)\ndescription | longtext\nreply | longtext\ndetailed_rating | longtext\ncreate_at | datetime\n\nacenanny_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_posts\n--------------\nName | Type\n\nid | int(11)\nstatus | tinyint(1)\ntype | text\ndata | text\nlinks | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\naddress | text\ncity | text\nzip | text\nstate | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_reports\n----------------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nnanny_id | int(11)\ndescription | mediumtext\n\nacenanny_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nacenanny_setting\n----------------\nName | Type\n\nid | int(11)\nsetting_key | varchar(255)\nsetting_value | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\nfeatures | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nacenanny_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nacenanny_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nacenanny_user_agreement\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nagreement_type | varchar(255)\nip_address | varchar(255)\nbrowser | varchar(255)\ncreate_at | datetime\n\nacenanny_verification_code\n--------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\nphone | varchar(255)\ncode | varchar(255)\ncreate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v3/api/custom/admin/nanny/profile/edit/:user_id', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n "}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n \n Used services: \n The code with comments is:\n ```\n let {\n email,\n phone,\n wechat,\n languages,\n country,\n city,\n region,\n birthyear,\n school,\n degree,\n study_field,\n intro,\n profile_picture,\n additional_pictures,\n certificates,\n attribute_details,\n interview,\n nanny_type,\n location_type,\n locations,\n employment_type,\n start_date,\n schedule,\n experience_and_skills,\n account_status,\n status,\n first_name_other_lang,\n last_name_other_lang\n } = req.body;\n\n let user_id = req.params.user_id;\n let sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n console.log(req.body);\n\n sdk.setTable(\"user\");\n\n let exist = [];\n\n if (email) {\n exist = await sdk.get({ email });\n\n if (exist.length) {\n if (exist[0].id != user_id) throw new Error(\"Email Already Exists\");\n }\n }\n\n if (phone) {\n exist = await sdk.get({ phone });\n\n if (exist.length) {\n if (exist[0].id != user_id) throw new Error(\"Phone Already Exists\");\n }\n }\n\n let user = await sdk.get({ id: user_id });\n\n await sdk.update(\n {\n email: email ?? user[0].email,\n phone: phone ?? user[0].phone\n },\n user_id\n );\n\n sdk.setTable(\"nanny_profile\");\n\n let profile = await sdk.get({ user_id });\n\n if (!profile.length) throw new Error(\"User Doesn't Exists!\");\n\n let data = {};\n\n nanny_type = nanny_type ?? JSON.parse(profile[0].nanny_type);\n\n if (experience_and_skills) {\n for (let x of nanny_type) {\n let slug = x.toLowerCase().replace(/ /g, \"_\");\n console.log(\"***\", slug);\n data[slug] = {\n experience: experience_and_skills[slug]?.experience ?? null,\n us_experience: experience_and_skills[slug]?.us_experience ?? null,\n documented_experience: experience_and_skills[slug]?.documented_experience ? experience_and_skills[slug]?.documented_experience : [],\n recommendations: experience_and_skills[slug]?.recommendations ? experience_and_skills[slug]?.recommendations : [],\n payment_options: experience_and_skills[slug]?.payment_options\n ? formatPaymentOptions(experience_and_skills[slug].payment_options)\n : formatPaymentOptions({})\n };\n }\n }\n\n sdk.setTable(\"nanny_profile\");\n\n await sdk.updateWhere(\n {\n wechat: wechat ?? profile[0].wechat,\n first_name_other_lang: first_name_other_lang ?? profile[0].first_name_other_lang,\n last_name_other_lang: last_name_other_lang ?? profile[0].last_name_other_lang,\n languages: languages ? JSON.stringify(languages) : profile[0].languages,\n country: country ?? profile[0].country,\n city: city ?? profile[0].city,\n interview: interview ?? profile[0].interview,\n region: region ?? profile[0].region,\n birthyear: birthyear ?? profile[0].birthyear,\n school: school ?? profile[0].school,\n degree: degree ?? profile[0].degree,\n study_field: study_field ?? profile[0].study_field,\n intro: intro ?? profile[0].intro,\n profile_picture: profile_picture ?? profile[0].profile_picture,\n additional_pictures: additional_pictures ? JSON.stringify(additional_pictures) : profile[0].additional_pictures,\n certificates: certificates ? JSON.stringify(certificates) : profile[0].certificates,\n attribute_details: attribute_details ? JSON.stringify(attribute_details) : profile[0].attribute_details,\n nanny_type: JSON.stringify(nanny_type),\n location_type: location_type ?? profile[0].location_type,\n locations: locations ? JSON.stringify(locations) : profile[0].locations,\n employment_type: employment_type ? JSON.stringify(employment_type) : profile[0].employment_type,\n start_date: start_date ? sqlDateFormat(new Date(start_date)) : profile[0].start_date,\n schedule: schedule ? shceduleFormatting(schedule) : profile[0].schedule,\n experience_and_skills: experience_and_skills ? JSON.stringify(data) : profile[0].experience_and_skills,\n account_status: account_status ?? profile[0].account_status,\n status: status ?? profile[0].status,\n update_at: sqlDateTimeFormat(new Date())\n },\n { user_id }\n );\n\n return res.json({\n error: false,\n message: \"Updated\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/lawfirm/invoice', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n "}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get invoices of a lawfirm.\n Used services: \n The code with comments is:\n ```\n const { law_firm_id, date } = req.body;\n if (!law_firm_id || typeof law_firm_id !== \"number\") return res.status(400).json({ error: true, message: \"Lawfirm ID is required/invalid\" });\n\n date ? req.body.where.push(`DATE_FORMAT(generated_at, '%Y-%M') = DATE_FORMAT('${date}', '%Y-%M')`) : \"\";\n\n const commonSql = `FROM forkfirm_invoice LEFT JOIN forkfirm_law_firm_client ON forkfirm_law_firm_client.client_id = forkfirm_invoice.client_id LEFT JOIN forkfirm_user ON forkfirm_user.id = forkfirm_invoice.client_id WHERE law_firm_id = ${law_firm_id} AND `;\n\n const sqlCount = `SELECT COUNT(*) OVER() AS num ${commonSql}`;\n const sqlSelect = `SELECT forkfirm_invoice.*, forkfirm_user.first_name, forkfirm_user.last_name ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/lawfirm/edit-rate', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n API to Edit Client's Rates"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Fetch the user from db and if it's lawfirm allow it to update the client's rate.\n Used services: - sdk\n- sdk.setProjectId\n- sdk.setTable\n- sdk.update\n The code with comments is:\n ```\n if (req.role !== \"lawfirm\") return res.status(403).json({ error: true, message: \"Unauthorized User\" });\n\n const { client_id, id, rate } = req.body;\n\n const sql = `SELECT forkfirm_user.* FROM forkfirm_profile LEFT JOIN forkfirm_user ON forkfirm_profile.invited_by = forkfirm_user.email WHERE forkfirm_profile.user_id = ${client_id} AND `;\n req.params.method = \"RAW\";\n const result = await handleReq(req, res, [], null, sql);\n\n if (result[0].role === \"admin\") return res.status(403).json({ error: true, message: \"You are not authorized to update this client's rate!\" });\n\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"client_rate\");\n await sdk.update({ rate }, id);\n\n res.json({ error: false, message: \"Updated Successfully!\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/client/invoice-details', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n API to Get Invoice Details"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get invoice details with pagination.\n Used services: \n The code with comments is:\n ```\n const { date } = req.body;\n if (!date) return res.status(400).json({ error: true, message: \"Date is required\" });\n\n const commonSql = `FROM forkfirm_contract LEFT JOIN forkfirm_user AS client ON client.id = forkfirm_contract.client_id JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.attorney_id = forkfirm_contract.attorney_id LEFT JOIN forkfirm_user AS lawfirm ON lawfirm.id = forkfirm_law_firm_attorney.law_firm_id LEFT JOIN forkfirm_doc_type ON forkfirm_doc_type.id = forkfirm_contract.doc_type_id LEFT JOIN forkfirm_client_rate ON forkfirm_client_rate.doc_type_id = forkfirm_doc_type.id AND forkfirm_client_rate.client_id = forkfirm_contract.client_id LEFT JOIN forkfirm_client_hours ON forkfirm_client_hours.contract_id = forkfirm_contract.id LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = lawfirm.id WHERE DATE_FORMAT(forkfirm_contract.create_at, '%Y-%m') = DATE_FORMAT(DATE_SUB('${date}', INTERVAL 0 MONTH), '%Y-%m') AND `;\n\n const sqlCount = `SELECT COUNT(*) AS num ${commonSql}`;\n\n const sqlSelect = `SELECT forkfirm_contract.*, lawfirm.first_name AS lawfirm_first_name, lawfirm.last_name AS lawfirm_last_name, lawfirm.email AS lawfirm_email, forkfirm_profile.base_office, client.first_name AS client_first_name, client.last_name AS client_last_name, client.email AS client_email, name, rate, hour, IF(contract_type, IF(delivery, rate * hour * ${localConfig.upCharge}, rate * hour + ${localConfig.standardCharge}), IF(delivery, rate * ${localConfig.upCharge}, rate + ${localConfig.standardCharge})) AS charged_amount, IF(contract_type, rate * hour, rate) AS total_rate ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/client/pay', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - API for Payment\n- Create payment intent\n- Store pi\n- Update invoice"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Gret invoice from db. Create payment intent then store the payment intent in db and update the invoice\n Used services: - sdk\n- stripe.paymentIntents\n- sdk\n- sdk.setProjectId\n- sdk.setTable\n- sdk.insert\n- sdk.setTable\n The code with comments is:\n ```\n const { invoice_id, payment_method } = req.body;\n\n if (!invoice_id || !payment_method) return res.status(400).json({ error: true, message: \"Invoice and Payment Method is required\" });\n\n const getInvoice = `SELECT forkfirm_invoice.* FROM forkfirm_invoice WHERE forkfirm_invoice.id = ${invoice_id} AND forkfirm_invoice.status = 0 AND `;\n req.params.method = \"RAW\";\n const invoiceResult = await handleReq(req, res, [], null, getInvoice);\n\n if (!invoiceResult.length) return res.status(400).json({ error: true, message: \"Invoice is not available\" });\n\n const userInfo = `SELECT id, stripe_uid FROM forkfirm_user WHERE forkfirm_user.id = ${invoiceResult[0].client_id} AND `;\n const userInfoResult = await handleReq(req, res, [], null, userInfo);\n\n // Create payment intent\n const paymentIntent = await stripe.paymentIntents.create({\n payment_method_types: [\"card\"],\n amount: invoiceResult[0].charged_amount * 100,\n currency: \"usd\",\n customer: userInfoResult[0].stripe_uid,\n payment_method: payment_method,\n off_session: true,\n confirm: true\n });\n\n if (paymentIntent.status !== \"succeeded\") return res.status(400).json({ error: true, message: \"Payment Failed!\" });\n\n if (paymentIntent.status === \"succeeded\") {\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n // Store pi\n sdk.setTable(\"stripe_payment_intent\");\n const stripeInvoiceResult = await sdk.insert({\n user_id: userInfoResult[0].id,\n stripe_id: paymentIntent.id,\n object: JSON.stringify(paymentIntent),\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n // Update invoice\n sdk.setTable(\"invoice\");\n await sdk.update(\n {\n status: 1,\n stripe_payment_intent_id: stripeInvoiceResult,\n paid_at: sqlDateTimeFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n },\n invoice_id\n );\n\n return res.status(200).json({ error: false, message: \"Payment Successful!\" });\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.get('/v2/api/forkfirm/get-cards/:userId', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - API to get Stripe cards"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n This api get's user's stripe uid from db and fetches all the cards of the user.\n Used services: - TokenMiddleware\n- ProjectMiddleware\n- HostMiddleware\n- sdk\n- sdk.get\n- stripeService.retrieveStripeCustomerAllCards\n The code with comments is:\n ```\n const sdk = req.sdk;\n const { after = null, before = null, limit = 10 } = req.query;\n const id = req.params.userId;\n\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"user\");\n\n const user = await sdk.get({ id: id });\n if (!user[0]) {\n return res.status(404).json({ error: true, message: \"User not found\" });\n }\n const customer_stripe_id = user[0].stripe_uid;\n if (!customer_stripe_id)\n return res.status(200).json({\n error: false,\n data: null,\n limit: limit\n });\n const cards = await stripeService.retrieveStripeCustomerAllCards({ customerId: customer_stripe_id, after, before, limit });\n\n res.status(200).json({\n error: false,\n data: cards,\n limit: limit\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/add-card/', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Add Stripe Cards"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get customer's stripe uid. If no stripe uid then create stripe customer with card else just add new card to the existing stripe customer.\n Used services: - sdk\n- ValidationService.validateObject\n- sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- stripeService.createStripeCustomerWithCard\n- sdk.update\n- stripeService.addNewCardToStripeCustomer\n The code with comments is:\n ```\n const sdk = req.sdk;\n const { sourceToken, userId } = req.body;\n\n const validationResult = await ValidationService.validateObject({ sourceToken: \"required\", userId: \"required\" }, { sourceToken, userId });\n if (validationResult.error) {\n return res.status(400).json(validationResult);\n }\n\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"user\");\n\n const user = await sdk.get({ id: userId });\n if (!user.length) {\n return res.status(404).json({ error: true, message: \"User not found\" });\n }\n\n const customerStripeId = user[0].stripe_uid;\n const metadata = {\n projectId: sdk.getProjectId()\n };\n if (!customerStripeId) {\n var customer = await stripeService.createStripeCustomerWithCard({\n email: user[0].email,\n tokenId: sourceToken,\n metadata\n });\n\n await sdk.update({ stripe_uid: customer.id }, user[0].id);\n } else {\n var customer = await stripeService.addNewCardToStripeCustomer({\n customerId: customerStripeId,\n tokenId: sourceToken,\n metadata\n });\n }\n\n res.status(200).json({\n error: false,\n model: customer,\n message: \"Customer card added successfully\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.delete('/v2/api/forkfirm/card/:userId/:id', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Delete Stripe Card"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Find customer's stripe uid from db and use it to delete crad from customer's stripe account\n Used services: - sdk\n- ValidationService.validateObject\n- sdk.get\n- stripeService.deleteStripeCustomerCard\n The code with comments is:\n ```\n // Delete Stripe Card\n const sdk = req.sdk;\n const { id: cardId, userId } = req.params;\n\n const validationResult = await ValidationService.validateObject({ cardId: \"required\" }, { cardId });\n if (validationResult.error) {\n return res.status(400).json(validationResult);\n }\n\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"user\");\n const user = await sdk.get({ id: userId });\n if (!user[0]) {\n return res.status(404).json({ error: true, message: \"User not found\" });\n }\n\n if (!user[0].stripe_uid) {\n return res.status(404).json({ error: true, message: \"User is not a stripe customer\" });\n }\n\n await stripeService.deleteStripeCustomerCard({\n customerId: user[0].stripe_uid,\n cardId: cardId\n });\n res.status(200).json({ error: false, message: \"Card deleted successfully\", isDeleted: true });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/attorney/client/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get Attorney's Clients"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get attornes of a clients with pagination\n Used services: \n The code with comments is:\n ```\n // Get Attorney's Clients\n const { where, page, limit, sortId, direction } = req.body;\n\n if ((page && typeof page !== \"number\") || (limit && typeof limit !== \"number\")) return res.status(400).json({ error: true, message: \"Invalid Request\" });\n\n if (sortId)\n req.body.sortId = sortId\n .split(\".\")\n .map((item) => `\\`${item}\\``)\n .join(\".\");\n\n if (direction) {\n if (direction === \"DESC\") req.body.direction = \"DESC\";\n else req.body.direction = \"ASC\";\n }\n\n if (where) {\n const searchStr = getSearchStr(where);\n req.body.where = searchStr;\n }\n\n const commonSql = `FROM forkfirm_attorney_client LEFT JOIN forkfirm_user AS client ON forkfirm_attorney_client.client_id = client.id LEFT JOIN forkfirm_user AS attorney ON attorney.id = forkfirm_attorney_client.attorney_id LEFT JOIN forkfirm_law_firm_attorney ON forkfirm_law_firm_attorney.attorney_id = forkfirm_attorney_client.attorney_id LEFT JOIN forkfirm_user AS lawfirm ON lawfirm.id = forkfirm_law_firm_attorney.law_firm_id LEFT JOIN forkfirm_profile ON forkfirm_profile.user_id = client.id LEFT JOIN forkfirm_photo ON forkfirm_photo.id = forkfirm_profile.playbook WHERE `;\n\n const sqlCount = `SELECT COUNT(*) AS num ${commonSql}`;\n\n const sqlSelect = `SELECT client.*, attorney.id AS attorney_id, lawfirm.id AS lawfirm_id, forkfirm_attorney_client.id AS forkfirm_attorney_client_id, url AS playbook, forkfirm_profile.id AS profile_id ${commonSql}`;\n\n req.params.method = \"PAGINATE\";\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/forkfirm/attorney/project/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - API to Get Attorney's Projects"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get attorney's projects with pagination\n Used services: \n The code with comments is:\n ```\n // API to Get Attorney's Projects\n const { where, page, limit, sortId, direction } = req.body;\n\n if ((page && typeof page !== \"number\") || (limit && typeof limit !== \"number\")) return res.status(400).json({ error: true, message: \"Invalid Request\" });\n\n if (sortId)\n req.body.sortId = sortId\n .split(\".\")\n .map((item) => `\\`${item}\\``)\n .join(\".\");\n\n if (direction) {\n if (direction === \"DESC\") req.body.direction = \"DESC\";\n else req.body.direction = \"ASC\";\n }\n\n if (where) {\n const searchStr = getSearchStr(where);\n req.body.where = searchStr;\n }\n\n const commonSql = `FROM forkfirm_project LEFT JOIN forkfirm_user ON forkfirm_user.id = forkfirm_project.client_id LEFT JOIN forkfirm_attorney_client ON forkfirm_attorney_client.client_id = forkfirm_project.client_id AND forkfirm_project.law_firm_id = (SELECT law_firm_id FROM forkfirm_law_firm_attorney WHERE attorney_id = forkfirm_attorney_client.attorney_id) LEFT JOIN forkfirm_photo AS pb ON pb.id = forkfirm_project.playbook LEFT JOIN forkfirm_photo AS pb2 ON pb2.id = forkfirm_project.playbook2 LEFT JOIN forkfirm_photo AS pb3 ON pb3.id = forkfirm_project.playbook3 LEFT JOIN forkfirm_photo AS nd ON nd.id = forkfirm_project.nda LEFT JOIN forkfirm_photo AS nd2 ON nd2.id = forkfirm_project.nda2 LEFT JOIN forkfirm_photo AS nd3 ON nd3.id = forkfirm_project.nda3 WHERE `;\n\n const sqlCount = `SELECT COUNT(*) OVER() AS num ${commonSql}`;\n const sqlSelect = `SELECT forkfirm_project.*, first_name, last_name, pb.url AS playbook_url, pb2.url AS playbook2_url, pb3.url AS playbook3_url, nd.url AS nda_url, nd2.url AS nda2_url, nd3.url AS nda3_url ${commonSql}`; // Note: Client's name\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n forkfirm_invoice_cronjob\n------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_photo\n--------------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_token\n--------------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nforkfirm_profile\n----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\nbase_office | varchar(255)\nattorney_expiry_date | date\nattorney_license | int(11)\nwebsite | text\ninvited_by | varchar(255)\nplaybook | int(11)\nemailed | tinyint(1)\n\nforkfirm_activity_log\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\ndate | date\nupdate_at | datetime\ncontract_id | int(11)\nstatus | int(11)\n\nforkfirm_stripe_payment_intent\n------------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_trigger_rules\n----------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice\n----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ngenerated_at | date\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\nupdate_at | datetime\ncreate_at | date\n\nforkfirm_chat\n-------------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_room\n-------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nchat_id | int(11)\nunread | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\n\nforkfirm_stripe_checkout\n------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email\n--------------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\nstatus | tinyint(1)\n\nforkfirm_trigger_type\n---------------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_order\n---------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_analytic_log\n---------------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_law_firm_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nclient_id | int(11)\n\nforkfirm_client_hours\n---------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nhour | int(11)\ncontract_id | int(11)\nlawfirm_id | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_email_template\n-----------------------\nName | Type\n\nid | int(11)\nemail_id | int(11)\nattorney_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_tag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ninput_type | int(11)\ninput_duration | int(11)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_setting\n-----------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_cms\n------------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_tag\n-------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ntag_id | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_webhook\n-----------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_document_hours\n--------------------------------\nName | Type\n\nid | int(11)\ncontract_id | int(11)\nclient_id | int(11)\nhour | int(11)\ndate | date\ndescription | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_invoice_current\n------------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_invoice_lawfirm\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ngenerated_at | date\nlawfirm_id | int(11)\ncharged_amount | double\nfork_fee | double\npayout_amount | double\nstatus | int(11)\npaid_at | date\nstripe_payment_intent_id | int(11)\n\nforkfirm_test\n-------------\nName | Type\n\nid | int(11)\n\nforkfirm_invoice_all\n--------------------\nName | Type\n\nid | int(11)\nemail | varchar(255)\ngenerated_at | date\ncharged_amount | decimal(44,1)\nfork_fee | decimal(50,5)\npayout_amount | decimal(51,5)\n\nforkfirm_doc_type\n-----------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndefault_type | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_price\n---------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_contract_tag\n---------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncontract_id | int(11)\ntag_id | int(11)\ninput_type | int(11)\ninput_duration | varchar(255)\nstatus | tinyint(1)\n\nforkfirm_contract\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nexecuted_at | date\nterminated_at | date\nupdate_at | datetime\ndate_updated | datetime\nactivity_status_updated_at | datetime\nclient_id | int(11)\noriginator | int(11)\noriginator_name | varchar(255)\nproject_name | varchar(255)\nattorney_id | int(11)\ncounter_party_name | varchar(255)\ncounter_party_known | tinyint(1)\ncounter_party_email | longtext\ncc | longtext\ndoc_type_id | int(11)\nexecuted_doc_type_id | int(11)\nturnaround_time | int(11)\ndelivery | int(11)\nstatus | int(11)\ncurrent_activity_status | int(11)\ndocument_id | int(11)\ndocument_id2 | int(11)\ndocument_id3 | int(11)\nexecuted_document_id | int(11)\nexecuted_document_id2 | int(11)\nexecuted_document_id3 | int(11)\ncontract_type | tinyint(1)\nemailed | tinyint(1)\n\nforkfirm_stripe_invoice\n-----------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_user\n-------------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_stripe_subscription\n----------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_permission\n-------------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | text\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_rate\n--------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\ndoc_type_id | int(11)\nrate | int(11)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_client_subclient\n-------------------------\nName | Type\n\nid | int(11)\nclient_id | int(11)\nsubclient_id | int(11)\nshow_billing | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nforkfirm_attorney_client\n------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nattorney_id | int(11)\nclient_id | int(11)\n\nforkfirm_law_firm_attorney\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nlaw_firm_id | int(11)\nattorney_id | int(11)\n\nforkfirm_project\n----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\nclient_id | int(11)\nlaw_firm_id | int(11)\nlaunch_date | date\nno_of_parties_outreach | int(11)\nplaybook | int(11)\nplaybook2 | int(11)\nplaybook3 | int(11)\nnda | int(11)\nnda2 | int(11)\nnda3 | int(11)\n\nforkfirm_stripe_product\n-----------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.get('/v2/api/forkfirm/attorney/contract/:contractId/:activityStatus', async function (req, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - API to Update Pending Contract"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Update the status of the pending contract\n Used services: - sdk\n- sdk.update\n- sdk.setProjectId\n- sdk.setTable\n The code with comments is:\n ```\n // API to Update Pending Contract\n const { contractId, activityStatus } = req.params;\n\n if ((contractId && typeof +contractId !== \"number\") || (activityStatus && typeof +activityStatus !== \"number\"))\n return res.status(400).json({ error: true, message: \"Invalid Request\" });\n\n const sdk = req.app.get(\"sdk\");\n sdk.setProjectId(\"forkfirm\");\n sdk.setTable(\"contract\");\n await sdk.update(\n {\n current_activity_status: +activityStatus,\n activity_status_updated_at: sqlDateTimeFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n },\n contractId\n );\n\n res.redirect(\"https://forkfirm.manaknightdigital.com/\");\n // return res.json({ error: false, message: \"Contract Activity Status Updated Successfully\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API to get Properties"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get properties with pagination\n Used services: \n The code with comments is:\n ```\n // Custom API to get Properties\n const tables = [\"property\", \"user\", \"property_spaces\"];\n const joinFields = [\"host_id\", \"id\", \"property_id\"];\n const renamedTables = tables.map((table) => `ergo_${table}`);\n\n const sqlCount = `SELECT COUNT(*) as num FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[1]}.${joinFields[1]} = ${renamedTables[0]}.${joinFields[0]} WHERE `;\n\n const sqlSelect = `SELECT ergo_property.*, email, (SELECT COALESCE(COUNT(ergo_property_spaces.id), 0) FROM ${renamedTables[2]} WHERE ${renamedTables[2]}.${joinFields[2]} = ${renamedTables[0]}.${joinFields[1]}) AS spaces FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[1]}.${joinFields[1]} = ${renamedTables[0]}.${joinFields[0]} WHERE `;\n\n return await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-spaces/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Spaces"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get property space with pagination if method is pagination. if method is post insert property space and update space count.\n Used services: - sdk\n- sdk.setProjectId\n- sdk.setTable\n- existPermission\n- sdk.get\n- sdk.insert\n The code with comments is:\n ```\n // Custom API for Property Spaces\n const tables = [\"property_spaces\", \"property\", \"spaces\"];\n const commonSql = `FROM ergo_property_spaces LEFT JOIN ergo_property ON ergo_property_spaces.property_id = ergo_property.id LEFT JOIN ergo_spaces ON ergo_property_spaces.space_id = ergo_spaces.id LEFT JOIN ergo_photo ON ergo_photo.id = ergo_property_spaces.default_image_id LEFT JOIN ergo_review ON ergo_review.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_user ON ergo_user.id = ergo_property.host_id LEFT JOIN ergo_property_spaces_schedule_template ON ergo_property_spaces_schedule_template.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_property_spaces_images ON ergo_property_spaces_images.property_spaces_id = ergo_property_spaces.id AND ergo_property_spaces.default_image_id = ergo_property_spaces_images.photo_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) OVER() as num ${commonSql}`;\n\n const sqlSelect = `SELECT ergo_property_spaces.*, ergo_spaces.category AS space_category, ergo_property.name AS property_name, ergo_photo.url AS default_image_url, AVG(ergo_review.space_rating) AS average_space_rating, schedule_template_id, email ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") return await handleReq(req, res, tables, sqlCount, sqlSelect, \" ergo_property_spaces.id \");\n\n const sdk = req.sdk;\n sdk.getDatabase();\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"permission\");\n const permission = await sdk.get({\n role: req.role\n });\n\n const acl = JSON.parse(permission[0].permission);\n\n switch (req.params.method) {\n case \"POST\":\n if (existPermission(acl, \"property_spaces\", \"POST\")) {\n const payload = await preProcessReq(req, res, sdk, acl, \"property_spaces\", \"POST\");\n if (!payload) return;\n\n // Part: Define Tax If Not Provided\n if (!payload.tax) {\n sdk.setTable(\"settings\");\n const [settings] = await sdk.get({\n key_name: \"tax\"\n });\n\n if (!settings)\n return res.status(400).json({\n error: true,\n message: \"Vat is not defined.\"\n });\n\n req.body.tax = settings.key_value;\n }\n\n const insertResult = await sdk.insert({\n ...payload,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n sdk.setTable(\"property\");\n req.params.method = \"RAW\";\n const rawQuery = `UPDATE ergo_property SET spaces = spaces + 1 WHERE id = ${payload.property_id} AND `;\n await handleReq(req, res, [\"property\"], null, rawQuery, null, QueryTypes.UPDATE);\n\n return res.status(200).json({\n error: false,\n message: insertResult\n });\n } else {\n return res.status(403).json({\n error: true,\n message: \"Invalid Resource\"\n });\n }\n break;\n\n default:\n res.status(403);\n res.json({\n error: true,\n message: \"Invalid Method\"\n });\n break;\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-space-images', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Space Images"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get property space images from db\n Used services: - ProjectMiddleware\n- UrlMiddleware\n- HostMiddleware\n The code with comments is:\n ```\n // Custom API for Property Space Images\n const tables = [\"property_spaces_images\", \"property\", \"spaces\", \"photo\", \"property_spaces\"];\n\n const commonSql = `FROM ergo_property_spaces_images LEFT JOIN ergo_property ON ergo_property_spaces_images.property_id = ergo_property.id LEFT JOIN ergo_property_spaces ON ergo_property_spaces_images.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_spaces ON ergo_spaces.id = ergo_property_spaces.space_id LEFT JOIN ergo_photo ON ergo_property_spaces_images.photo_id = ergo_photo.id LEFT JOIN ergo_user ON ergo_user.id = ergo_property.host_id LEFT JOIN ergo_profile ON ergo_profile.user_id = ergo_user.id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n const sqlSelect = `SELECT ergo_property_spaces_images.*, ergo_spaces.category AS space_category, ergo_property.name AS property_name, ergo_photo.url AS photo_url, IF(default_image_id = photo_id, 1, 0) AS default_image, email, settings ${commonSql}`;\n\n await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-spaces-amenitites/:method', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Space Amenities"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query db to get property space amenities\n Used services: - ProjectMiddleware\n- UrlMiddleware\n- HostMiddleware\n The code with comments is:\n ```\n // Custom API for Property Space Amenities\n const tables = [\"property_spaces_amenitites\", \"property_spaces\", \"amenity\", \"property\", \"spaces\"];\n\n const commonSql = `FROM ergo_property_spaces_amenitites LEFT JOIN ergo_property_spaces ON ergo_property_spaces_amenitites.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_amenity ON ergo_property_spaces_amenitites.amenity_id = ergo_amenity.id LEFT JOIN ergo_property ON ergo_property_spaces.property_id = ergo_property.id LEFT JOIN ergo_spaces ON ergo_property_spaces.space_id = ergo_spaces.id LEFT JOIN ergo_user ON ergo_user.id = ergo_property.host_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n const sqlSelect = `SELECT ergo_property_spaces_amenitites.*, ergo_amenity.name AS amenity_name, ergo_property.name AS property_name, ergo_spaces.category AS space_category, email ${commonSql}`;\n\n return await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-addons/:method', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Add-Ons"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Returns addons of the property\n Used services: - ProjectMiddleware\n- UrlMiddleware\n- HostMiddleware\n The code with comments is:\n ```\n // Custom API for Property Add-Ons\n const tables = [\"property_add_on\", \"property\", \"add_on\"];\n\n const commonSql = `FROM ergo_property_add_on LEFT JOIN ergo_property ON ergo_property_add_on.property_id = ergo_property.id LEFT JOIN ergo_add_on ON ergo_property_add_on.add_on_id = ergo_add_on.id LEFT JOIN ergo_user ON ergo_user.id = ergo_property.host_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n const sqlSelect = `SELECT ergo_property_add_on.*, ergo_property.name AS property_name, ergo_add_on.name AS add_on_name, cost, email ${commonSql}`;\n\n await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/booking-addon', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Booking Add-Ons"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get booking addons from the db\n Used services: \n The code with comments is:\n ```\n // Custom API for Booking Add-Ons\n const tables = [\"booking_addons\", \"booking\", \"property_add_on\", \"add_on\", \"property\", \"property_spaces\", \"spaces\"];\n const joinFields = [\"booking_id\", \"id\", \"property_add_on_id\", \"add_on_id\", \"property_id\", \"space_id\", \"property_space_id\"];\n const renamedTables = tables.map((table) => `ergo_${table}`);\n\n const sqlCount = `SELECT COUNT(*) as num FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[0]} = ${renamedTables[1]}.${joinFields[1]} LEFT JOIN ${renamedTables[2]} ON ${renamedTables[0]}.${joinFields[2]} = ${renamedTables[2]}.${joinFields[1]} LEFT JOIN ${renamedTables[3]} ON ${renamedTables[2]}.${joinFields[3]} = ${renamedTables[3]}.${joinFields[1]} LEFT JOIN ${renamedTables[4]} ON ${renamedTables[2]}.${joinFields[4]} = ${renamedTables[4]}.${joinFields[1]} LEFT JOIN ${renamedTables[5]} ON ${renamedTables[1]}.${joinFields[6]} = ${renamedTables[5]}.${joinFields[1]} LEFT JOIN ${renamedTables[6]} ON ${renamedTables[5]}.${joinFields[5]} = ${renamedTables[6]}.${joinFields[1]} WHERE `;\n\n const sqlSelect = `SELECT ergo_booking_addons.*, ergo_add_on.name AS add_on_name, ergo_property.name AS property_name, ergo_spaces.category AS space_category FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[0]} = ${renamedTables[1]}.${joinFields[1]} LEFT JOIN ${renamedTables[2]} ON ${renamedTables[0]}.${joinFields[2]} = ${renamedTables[2]}.${joinFields[1]} LEFT JOIN ${renamedTables[3]} ON ${renamedTables[2]}.${joinFields[3]} = ${renamedTables[3]}.${joinFields[1]} LEFT JOIN ${renamedTables[4]} ON ${renamedTables[2]}.${joinFields[4]} = ${renamedTables[4]}.${joinFields[1]} LEFT JOIN ${renamedTables[5]} ON ${renamedTables[1]}.${joinFields[6]} = ${renamedTables[5]}.${joinFields[1]} LEFT JOIN ${renamedTables[6]} ON ${renamedTables[5]}.${joinFields[5]} = ${renamedTables[6]}.${joinFields[1]} WHERE `;\n\n await handleReq(req, res, tables, sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.get('/v2/api/custom/ergo/google-auth', async function (req, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Google OAuth Invoked by Google"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Google OAuth endpoint Invoked by Google\n Used services: \n The code with comments is:\n ```\n // Google OAuth Invoked by Google\n const code = req.query.code;\n const options = {\n code,\n client_id: localConfig.GOOGLE_CLIENT_ID,\n client_secret: localConfig.GOOGLE_CLIENT_SECRET,\n redirect_uri: localConfig.GOOGLE_REDIRECT_URI,\n grant_type: localConfig.GOOGLE_GRANT_TYPE\n };\n\n const result = await fetch(localConfig.GOOGLE_TOKEN_URI, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\"\n },\n body: new URLSearchParams(options)\n });\n\n const data = await result.json();\n\n res.redirect(localConfig.OAUTH_SUCCESS_REDIRECT_URI + new URLSearchParams(data));\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/google-calendar-sync', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Google Calendar Sync\n- Send email notification 24 hours before the event\n- Show popup notification 10 minutes before the event\n"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Sync booking events to user's google calendar\n Used services: \n The code with comments is:\n ```\n // Google Calendar Sync\n const { access_token, expires_in, refresh_token, scope, token_type, summary, location, description, startTime, endTime, attendees } = req.body;\n\n async function createEvent(auth) {\n const calendar = google.calendar({ version: \"v3\", auth });\n\n const event = {\n summary,\n location,\n description,\n start: {\n dateTime: startTime,\n timeZone: \"UTC\"\n },\n end: {\n dateTime: endTime,\n timeZone: \"UTC\"\n },\n attendees: attendees.map((attendee) => ({ email: attendee })),\n reminders: {\n useDefault: false,\n overrides: [\n { method: \"email\", minutes: 24 * 60 }, // Remark: Send email notification 24 hours before the event\n { method: \"popup\", minutes: 10 } // Remark: Show popup notification 10 minutes before the event\n ]\n }\n };\n\n calendar.events.insert(\n {\n auth: auth,\n calendarId: \"primary\",\n resource: event\n },\n function (err, event) {\n if (err) {\n console.log(\"There was an error contacting the Calendar service: \" + err);\n return;\n }\n\n res.status(201).json({\n error: false,\n message: \"Event Created!\",\n eventLink: event.data.htmlLink\n });\n }\n );\n }\n\n const oauth2Client = new google.auth.OAuth2(localConfig.GOOGLE_CLIENT_ID, localConfig.GOOGLE_CLIENT_SECRET, localConfig.GOOGLE_REDIRECT_URI);\n oauth2Client.setCredentials({ access_token, expires_in, refresh_token, scope, token_type });\n\n await createEvent(oauth2Client);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/review/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Review"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Paginate, Create or Update review based on the associated method\n Used services: - sdk\n- sdk.get\n- sdk.insert\n\n The code with comments is:\n ```\n // Custom API for Review\n const { user } = req.body;\n\n let tables, joinFields, renamedTables, commonSql, sqlCount, sqlSelect;\n\n const sdk = req.sdk;\n sdk.getDatabase();\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"permission\");\n const permission = await sdk.get({\n role: req.role\n });\n\n const acl = JSON.parse(permission[0].permission);\n\n switch (req.params.method) {\n case \"PAGINATE\":\n tables = [\"review\", \"booking\", \"user\", \"property_spaces\", \"spaces\", \"property\"];\n joinFields = [\"booking_id\", \"id\", \"host_id\", \"customer_id\", \"property_spaces_id\", \"space_id\", \"property_id\"];\n renamedTables = tables.map((table) => `ergo_${table}`);\n\n commonSql = `FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[0]} = ${renamedTables[1]}.${joinFields[1]} LEFT JOIN ${renamedTables[2]} AS host ON host.${joinFields[1]} = ${renamedTables[0]}.${joinFields[2]} LEFT JOIN ${renamedTables[2]} AS customer ON customer.${joinFields[1]} = ${renamedTables[0]}.${joinFields[3]} LEFT JOIN ${renamedTables[3]} ON ${renamedTables[3]}.${joinFields[1]} = ${renamedTables[0]}.${joinFields[4]} LEFT JOIN ${renamedTables[4]} ON ${renamedTables[4]}.${joinFields[1]} = ${renamedTables[3]}.${joinFields[5]} LEFT JOIN ${renamedTables[5]} ON ${renamedTables[5]}.${joinFields[1]} = ${renamedTables[3]}.${joinFields[6]} WHERE `;\n\n sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n sqlSelect = `SELECT ergo_review.*, ergo_booking.booking_start_time, ergo_spaces.category, ergo_property.name, host.first_name AS host_first_name, host.last_name AS host_last_name, customer.first_name AS customer_first_name, customer.last_name AS customer_last_name, (CASE WHEN given_by = 'host' THEN customer_rating ELSE host_rating END) AS rating, (CASE WHEN given_by = '${user}' THEN 'given' ELSE 'received' END) AS type ${commonSql}`;\n\n return await handleReq(req, res, tables, sqlCount, sqlSelect);\n case \"get-hashtag\":\n tables = [\"review_hashtag\", \"hashtag\"];\n joinFields = [\"hashtag_id\", \"id\"];\n renamedTables = tables.map((table) => `ergo_${table}`);\n\n sqlSelect = `SELECT ergo_review_hashtag.*, ergo_hashtag.name FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[0]} = ${renamedTables[1]}.${joinFields[1]} WHERE `;\n\n req.params.method = \"RAW\";\n const result = await handleReq(req, res, tables, null, sqlSelect);\n\n return res.status(200).json({\n error: false,\n list: result\n });\n case \"POST\":\n if (existPermission(acl, \"review\", \"POST\")) {\n const payload = await preProcessReq(req, res, sdk, acl, \"review\", \"POST\");\n if (!payload) return;\n\n payload.post_date && (payload.post_date = sqlDateTimeFormat(new Date(payload.post_date)));\n\n const insertResult = await sdk.insert({\n ...payload,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n return res.status(200).json({\n error: false,\n message: insertResult\n });\n } else {\n return res.status(403).json({\n error: true,\n message: \"Invalid Resource\"\n });\n }\n case \"PUT\":\n if (existPermission(acl, \"review\", \"PUT\")) {\n const payload = await preProcessReq(req, res, sdk, acl, \"review\", \"PUT\");\n\n if (!payload) return;\n\n if (!req.body.id) {\n return res.status(403).json({\n error: true,\n message: \"ID Missing\",\n validation: { id: \"id missing\" }\n });\n }\n\n const id = req.body.id;\n delete payload.id;\n\n payload.post_date && (payload.post_date = sqlDateTimeFormat(new Date(payload.post_date)));\n\n await sdk.update(\n {\n ...payload,\n update_at: sqlDateTimeFormat(new Date())\n },\n id\n );\n return res.status(200).json({\n error: false,\n message: id\n });\n } else {\n return res.status(403).json({\n error: true,\n message: \"Invalid Resource\"\n });\n }\n default:\n res.status(403);\n res.json({\n error: true,\n message: \"Invalid Method\"\n });\n break;\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/scheduling/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Scheduling"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get property space's scheduling\n Used services: \n The code with comments is:\n ```\n // Custom API for Scheduling\n const tables = [\"property_spaces\", \"property\", \"spaces\", \"user\"];\n const joinFields = [\"host_id\", \"id\", \"property_id\", \"space_id\"];\n const renamedTables = tables.map((table) => `ergo_${table}`);\n\n const commonSql = `FROM ${renamedTables[0]} LEFT JOIN ${renamedTables[1]} ON ${renamedTables[0]}.${joinFields[2]} = ${renamedTables[1]}.${joinFields[1]} LEFT JOIN ${renamedTables[2]} ON ${renamedTables[2]}.${joinFields[1]} = ${renamedTables[0]}.${joinFields[3]} LEFT JOIN ${renamedTables[3]} ON ${renamedTables[3]}.${joinFields[1]} = ${renamedTables[1]}.${joinFields[0]} WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT ergo_property_spaces.*, ergo_user.first_name AS host_first_name, ergo_user.last_name AS host_last_name, ergo_property.name AS property_name, ergo_spaces.category AS space_category ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n return await handleReq(req, res, tables, sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/property-spaces-schedule-template/:method', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Custom API for Property Spaces Schedule Template"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Query the db to get property space schedule template\n Used services: \n The code with comments is:\n ```\n // Custom API for Property Spaces Schedule Template\n const commonSql = `FROM ergo_property_spaces_schedule_template LEFT JOIN ergo_property_spaces ON ergo_property_spaces_schedule_template.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_schedule_template ON ergo_schedule_template.id = ergo_property_spaces_schedule_template.schedule_template_id WHERE `;\n\n const sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n\n const sqlSelect = `SELECT ergo_property_spaces_schedule_template.*, template_name, host_id ${commonSql}`;\n\n if (req.params.method === \"PAGINATE\") {\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/top-hosts', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get Top Rated Hosts"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Returns top rated hosts with pagination\n Used services: \n The code with comments is:\n ```\n // Get Top Rated Hosts\n const commonSql = `FROM ergo_user LEFT JOIN ergo_profile ON ergo_profile.user_id = ergo_user.id LEFT JOIN ergo_photo ON ergo_photo.id = ergo_user.photo LEFT JOIN ergo_property ON ergo_property.host_id = ergo_user.id LEFT JOIN ergo_property_spaces ON ergo_property_spaces.property_id = ergo_property.id LEFT JOIN ergo_spaces ON ergo_spaces.id = ergo_property_spaces.space_id WHERE role = \"host\" AND `;\n\n const sqlCount = `SELECT DISTINCT COUNT(*) OVER() AS num ${commonSql}`;\n\n const sqlSelect = `SELECT ergo_user.id, role, first_name, last_name, email, phone, photo, ergo_profile.address_line_1, ergo_profile.address_line_2, ergo_profile.city, ergo_profile.country, ergo_profile.zip, ROUND((SELECT AVG(IF(status = 1, host_rating, NULL)) FROM ergo_review WHERE host_id = ergo_user.id), 2) AS avg_host_rating, (SELECT SUM(status = 1) FROM ergo_review WHERE host_id = ergo_user.id AND host_rating IS NOT NULL) AS rating_count ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect, \" ergo_user.id \");\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/review-hashtag/:method', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get Reviews with Hashtags"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get reviews along with hashtags\n Used services: \n The code with comments is:\n ```\n // Get Reviews with Hashtags\n const { user } = req.body;\n\n switch (req.params.method) {\n case \"PAGINATE\":\n\n commonSql = `FROM ergo_review LEFT JOIN ergo_booking ON ergo_review.booking_id = ergo_booking.id LEFT JOIN ergo_user AS host ON host.id = ergo_review.host_id LEFT JOIN ergo_user AS customer ON customer.id = ergo_review.customer_id LEFT JOIN ergo_property_spaces ON ergo_property_spaces.id = ergo_review.property_spaces_id LEFT JOIN ergo_spaces ON ergo_spaces.id = ergo_property_spaces.space_id LEFT JOIN ergo_property ON ergo_property.id = ergo_property_spaces.property_id LEFT JOIN ergo_review_hashtag ON ergo_review_hashtag.review_id = ergo_review.id LEFT JOIN ergo_hashtag ON ergo_hashtag.id = ergo_review_hashtag.hashtag_id WHERE `;\n\n sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n\n sqlSelect = `SELECT ergo_review.*, ergo_booking.booking_start_time, ergo_spaces.category, ergo_property.name, host.first_name AS host_first_name, host.last_name AS host_last_name, host.is_photo_approved AS host_photo_approved, customer.first_name AS customer_first_name, customer.last_name AS customer_last_name, customer.is_photo_approved AS customer_photo_approved, (CASE WHEN given_by = 'host' THEN host.photo ELSE customer.photo END) AS photo, (CASE WHEN given_by = 'host' THEN customer_rating ELSE host_rating END) AS rating, (CASE WHEN given_by = '${user}' THEN 'given' ELSE 'received' END) AS type, GROUP_CONCAT(ergo_hashtag.name) AS hashtags ${commonSql}`;\n\n await handleReq(req, res, [], sqlCount, sqlSelect, \" ergo_review.id \");\n break;\n default:\n res.status(403);\n res.json({\n error: true,\n message: \"Invalid Method\"\n });\n break;\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/favourite/:method', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get Favourite Property Spaces"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get favourite property spaces\n Used services: \n The code with comments is:\n ```\n // Get Favourite Property Spaces\n switch (req.params.method) {\n case \"PAGINATE\":\n commonSql = `FROM ergo_property_spaces INNER JOIN ergo_user_property_spaces ON ergo_user_property_spaces.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_property ON ergo_property.id = ergo_property_spaces.property_id LEFT JOIN ergo_spaces ON ergo_spaces.id = ergo_property_spaces.space_id LEFT JOIN ergo_review ON ergo_review.property_spaces_id = ergo_property_spaces.id LEFT JOIN ergo_photo ON ergo_photo.id = ergo_property_spaces.default_image_id WHERE `;\n\n sqlCount = `SELECT DISTINCT COUNT(*) OVER() as num ${commonSql}`;\n sqlSelect = `SELECT ergo_property_spaces.*, AVG(ergo_review.space_rating) AS average_space_rating, COUNT(ergo_review.space_rating) AS space_rating_count, ergo_property.name, category, ergo_property.address_line_1, ergo_property.address_line_2, ergo_property.city, ergo_property.country, ergo_property.zip, url ${commonSql}`;\n\n await handleReq(req, res, [], sqlCount, sqlSelect, \" ergo_property_spaces.id \");\n break;\n default:\n res.status(403);\n res.json({\n error: true,\n message: \"Invalid Method\"\n });\n break;\n }\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/pay', [middlewares], ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Calculate the total amount to be charged\n- Charge Immediately if Customer has Saved Card\n- Update the booking Based on the Payment Status\n- Add Booking to Queue Table"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Calculate the total amount to be charged and chrage the user if has saved card and update booking status and payment status. If user doesn't have payment method then create payment intent and return it.\n Used services: - ValidationService.validateInputMethod\n- sdk.setProjectId\n- sdk.setProjectId\n- sdk.setTable\n- sdk.update\n- sdk.insert\n\n The code with comments is:\n ```\n const validationResult = await ValidationService.validateInputMethod(\n {\n booking_id: \"required|integer\",\n user_id: \"required|integer\",\n stripe_uid: \"string\",\n payment_method: \"string\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { booking_id, user_id, stripe_uid, payment_method } = req.body;\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n // Calculate the total amount to be charged\n const sqlSelect = `SELECT ergo_user.id AS customer_id, ((ergo_booking.booked_unit * ergo_property_spaces.rate * (ergo_booking.duration / 3600) + (ergo_booking.duration / 3600) * num_guests * additional_guest_rate) + ((ergo_booking.booked_unit * ergo_property_spaces.rate * (ergo_booking.duration / 3600) + (ergo_booking.duration / 3600) * num_guests * additional_guest_rate) * ergo_booking.tax_rate)/100) + (SELECT COALESCE(SUM(ergo_add_on.cost), 0) FROM ergo_booking_addons LEFT JOIN ergo_property_add_on ON ergo_property_add_on.id = ergo_booking_addons.property_add_on_id LEFT JOIN ergo_add_on ON ergo_add_on.id = ergo_property_add_on.add_on_id WHERE ergo_booking.id = ergo_booking_addons.booking_id) AS total, payment_method, stripe_uid FROM ergo_booking LEFT JOIN ergo_property_spaces ON ergo_booking.property_space_id = ergo_property_spaces.id LEFT JOIN ergo_property ON ergo_property_spaces.property_id = ergo_property.id LEFT JOIN ergo_spaces ON ergo_property_spaces.space_id = ergo_spaces.id LEFT JOIN ergo_user ON ergo_booking.customer_id = ergo_user.id WHERE ergo_booking.id=${booking_id} AND `;\n\n req.params.method = \"RAW\";\n const [result] = await handleReq(req, res, [], null, sqlSelect);\n if (!result) return res.status(400).json({ error: true, message: \"No booking found!\" });\n\n // Charge Immediately if Customer has Saved Card\n if (stripe_uid && payment_method) {\n const paymentIntent = await stripe.paymentIntents.create({\n payment_method_types: [\"card\"],\n amount: Math.round(result.total * 100),\n currency: \"usd\",\n metadata: {\n projectId: req.projectId,\n booking_id,\n user_id,\n immediate: true\n },\n customer: stripe_uid,\n payment_method: payment_method,\n off_session: true,\n confirm: true\n });\n\n // Update the booking Based on the Payment Status\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"booking\");\n if (paymentIntent.status !== \"succeeded\") {\n await sdk.update(\n {\n status: 4,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n\n return res.status(403).json({ error: true, message: \"Payment Failed! Booking Declined!\" });\n }\n\n if (paymentIntent.status === \"succeeded\") {\n sdk.setTable(\"stripe_payment_intent\");\n const stripePIResult = await sdk.insert({\n user_id,\n stripe_id: paymentIntent.id,\n object: JSON.stringify(paymentIntent),\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n sdk.setTable(\"booking\");\n await sdk.update(\n {\n status: 2,\n payment_status: 1,\n stripe_payment_intent_id: stripePIResult,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n\n // Add Booking to Queue Table\n sdk.setTable(\"booking_queue\");\n await sdk.insert({\n booking_id,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n return res.status(200).json({ success: true, message: \"Booked!\" });\n }\n }\n\n const paymentIntent = await stripe.paymentIntents.create({\n amount: Math.round(result.total * 100),\n currency: \"usd\",\n automatic_payment_methods: { enabled: true },\n metadata: {\n projectId: req.projectId,\n booking_id,\n user_id\n }\n });\n\n return res.status(200).json({\n error: false,\n message: \"Success\",\n payment_intent: paymentIntent\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/refund', [middlewares], ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Refund Paymment\n- Cancell Booking\n- remove booking queue"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get user's payment info and use it to refund. Then cancel the booking.\n Used services: - sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- ValidationService.validateInputMethod\n- sdk.update\n- sdk.deleteWhere\n The code with comments is:\n ```\n const validationResult = await ValidationService.validateInputMethod(\n {\n booking_id: \"required|integer\",\n stripe_payment_intent_id: \"required|integer\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { booking_id, stripe_payment_intent_id } = req.body;\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n\n sdk.setTable(\"stripe_payment_intent\");\n const [stripePI] = await sdk.get({ id: stripe_payment_intent_id });\n if (!stripePI) return res.status(400).json({ error: true, message: \"No payment info found.\" });\n\n // Refund Paymment\n const refund = await stripe.refunds.create({\n payment_intent: stripePI.stripe_id\n // amount: 1000\n });\n\n if (refund.status !== \"succeeded\") return res.status(400).json({ error: true, message: \"Failed to refund!\" });\n\n // Cancell Booking\n sdk.setTable(\"booking\");\n await sdk.update(\n {\n status: 5,\n payment_status: 4,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n\n // remove booking queue\n sdk.setTable(\"booking_queue\");\n await sdk.deleteWhere({ booking_id });\n\n return res.status(200).json({ error: false, message: \"Booking Cancelled and Refunded!\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/webhook', async (req, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - If Not Success Event Set payment_status to Failed\n- Make Booking Upcoming"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n This webhook listens for the events coming from stripe and updates payment status and booking\n Used services: - sdk.setDatabase\n- sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- sdk.update\n- sdk.insert\n The code with comments is:\n ```\n const event = req.body;\n\n const payment = event.data.object;\n const { projectId, booking_id, user_id, immediate } = payment.metadata;\n\n if (projectId !== \"ergo\") return res.status(200).json({ success: true, message: \"The event is captured but it's not for this endpoint!\" });\n\n if (immediate) return res.status(200).json({ success: true, message: \"Successfully Booked!\" });\n\n if (!projectId || !booking_id) return res.status(400).json({ success: false, message: \"Project/Booking ID is missing!\" });\n\n const sdk = new ManaKnightSDK();\n sdk.setDatabase(config.databaseName);\n sdk.setProjectId(projectId);\n sdk.setTable(\"stripe_webhook\");\n\n const webhookHandledBefore = await sdk.get({ is_handled: true, stripe_id: event.id, event_type: event.type });\n if (webhookHandledBefore[0]) res.status(200).json({ success: true, message: \"Webhook handled before\" });\n\n // If Not Success Event Set payment_status to Failed\n if (event.type !== \"payment_intent.succeeded\") {\n sdk.setTable(\"booking\");\n await sdk.update(\n {\n payment_status: 3,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n return res.status(403).json({ success: false, message: \"Payment Failed! Booking Declined!\" });\n }\n\n // Make Booking Upcoming(1)\n sdk.setTable(\"stripe_payment_intent\");\n const stripePIResult = await sdk.insert({\n user_id,\n stripe_id: payment.id,\n object: JSON.stringify(payment),\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n sdk.setTable(\"booking\");\n await sdk.update(\n {\n payment_status: 1,\n stripe_payment_intent_id: stripePIResult,\n update_at: sqlDateTimeFormat(new Date())\n },\n booking_id\n );\n\n // sdk.setTable(\"booking_queue\");\n // await sdk.insert({\n // booking_id,\n // create_at: sqlDateFormat(new Date()),\n // update_at: sqlDateTimeFormat(new Date())\n // });\n\n sdk.setTable(\"stripe_webhook\");\n await sdk.insert({\n stripe_id: event.id,\n description: event.data.object.object,\n event_type: event.type,\n resource_type: event.type,\n object: JSON.stringify(event.data.object),\n is_handled: true,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n\n return res.status(200).json({ success: true, message: \"Booked!\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.get('/v2/api/custom/ergo/spaces', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Get All Spaces"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Return all spaces from db\n Used services: \n The code with comments is:\n ```\n // Get All Spaces\n if (req.body.where) req.body.where = [];\n const sqlSelect = `SELECT * from ergo_spaces WHERE `;\n\n req.params.method = \"RAW\";\n const result = await handleReq(req, res, [], null, sqlSelect);\n\n res.status(200).json({\n error: false,\n list: result\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v3/api/custom/ergo/device', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - If Password Reset Required Prevent Login\n- Check If User Is Active\n- Check If User Is Verified\n- Check If Already Logged In\n- Register Unknown Device\n- Update last_login_time"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate input then check if password reset required, user active and verified and already logged in. Store the unknown device and update last login time\n Used services: - sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- sdk.update\n- sdk.insert\n- ValidationService.validateInputMethod\n The code with comments is:\n ```\n const validationResult = await ValidationService.validateInputMethod(\n {\n uid: \"required\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { uid } = req.body;\n\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"user\");\n\n const [userInfo] = await sdk.get({ id: req.user_id });\n if (!userInfo || userInfo.deleted_at)\n return res.status(403).json({\n error: true,\n message: \"This email is not registered!\"\n });\n const { id, reset_pass } = userInfo;\n\n // If Password Reset Required Prevent Login\n if (reset_pass)\n return res.status(401).json({\n error: true,\n message: \"Please reset your password before trying to login.\"\n });\n\n // Check If User Is Active\n if (!userInfo.status)\n return res.status(403).json({\n error: true,\n message: \"Your account is inactive\"\n });\n\n // Check If User Is Verified\n if (!userInfo.verify)\n return res.status(403).json({\n error: true,\n message: \"Your account is not verified\"\n });\n\n // Check If Already Logged In\n sdk.setTable(\"device\");\n const [loggedIn] = await sdk.get({ user_id: id, active: 1 });\n if (loggedIn && loggedIn.uid !== uid) sdk.updateWhere({ active: 0 }, { user_id: id, active: 1 });\n\n // Register Unknown Device\n const [deviceInfo] = await sdk.get({ user_id: id, uid });\n\n if (!deviceInfo) {\n await sdk.insert({\n user_id: id,\n uid,\n last_login_time: sqlDateTimeFormat(new Date()),\n create_at: sqlDateTimeFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date())\n });\n } else {\n // Update last_login_time\n await sdk.update(\n {\n last_login_time: sqlDateTimeFormat(new Date()),\n active: 1,\n update_at: sqlDateTimeFormat(new Date())\n },\n deviceInfo.id\n );\n }\n\n return res.status(200).json({ error: false, message: \"Success\" });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v3/api/ergo/logout', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n "}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n It logs out user from a known device\n Used services: - ValidationService.validateInputMethod\n- sdk.setProjectId\n- sdk.setTable\n- sdk.updateWhere\n The code with comments is:\n ```\n const validationResult = await ValidationService.validateInputMethod(\n {\n uid: \"required\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { uid } = req.body;\n\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"device\");\n\n await sdk.updateWhere(\n {\n active: 0,\n update_at: sqlDateTimeFormat(new Date())\n },\n { user_id: req.user_id, uid }\n );\n\n return res.status(200).json({\n error: false,\n message: \"Successfully logged out\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.post('/v2/api/custom/ergo/id-verification', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n "}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Get id verification info\n Used services: \n The code with comments is:\n ```\n commonSql = `FROM ergo_id_verification LEFT JOIN ergo_user ON ergo_user.id = ergo_id_verification.user_id LEFT JOIN ergo_profile ON ergo_profile.user_id = ergo_user.id WHERE `;\n\n sqlCount = `SELECT COUNT(*) as num ${commonSql}`;\n sqlSelect = `SELECT ergo_id_verification.*, first_name, last_name, ergo_user.create_at AS user_create_at, role, ergo_user.status AS user_status, email, dob ${commonSql}`;\n\n return await handleReq(req, res, [], sqlCount, sqlSelect);\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/custom/ergo/confirm-delete-email', middlewares, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Send Delete Account Email"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Send a confirmation email to user after deleting account\n Used services: - ValidationService.validateInputMethod\n- JwtService.createAccessToken\n- sdk.setProjectId\n- sdk.setTable\n- sdk.get\n- MailService.initialize\n- MailService.send\n The code with comments is:\n ```\n // Send Delete Account Email\n const validationResult = await ValidationService.validateInputMethod(\n {\n email: \"required|email\",\n user_id: \"required|integer\",\n role: \"required|string\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { email, user_id, role } = req.body;\n\n const token = JwtService.createAccessToken(\n {\n user_id,\n role\n },\n config.jwt_expire,\n config.jwt_key\n );\n\n const sdk = req.sdk;\n\n let project;\n if (config.env == \"production\") {\n project = require(\"../../project\");\n } else {\n sdk.setProjectId(\"manaknight\");\n sdk.setTable(\"projects\");\n [project] = await sdk.get({\n project_id: req.projectId\n });\n }\n\n if (!project) return res.status(400).json({ error: true, message: \"Project not found\" });\n\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"email\");\n const [template] = await sdk.get({\n slug: \"delete-account\"\n });\n if (!template) return res.status(400).json({ error: true, message: \"Template not found\" });\n\n MailService.initialize(config);\n const { subject, html } = MailService.inject(\n { body: template.html, subject: template.subject },\n { link: `Link` }\n );\n\n const mailResult = await MailService.send(config.mail_user, email, subject, html);\n\n if (typeof mailResult === \"string\") {\n return res.status(403).json({\n error: true,\n message: mailResult\n });\n }\n if (mailResult.error) return res.status(403).json({ error: true, mailResult });\n\n return res.status(200).json({\n error: false,\n message: \"Confirmation email has been sent!\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v2/api/custom/ergo/reset', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Reset Password"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Validate input, check code compare hash and update password in db\n Used services: - ValidationService.validateInputMethod\n- ProjectMiddleware\n- UrlMiddleware\n- HostMiddleware\n- PasswordService.compareHash\n- AuthService\n- sdk.get\n- sdk.updateWhere\n The code with comments is:\n ```\n // Reset Password\n const validationResult = await ValidationService.validateInputMethod(\n {\n code: \"required|integer\",\n token: \"required|string\",\n password: \"required|string\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n let sdk = req.sdk;\n sdk.getDatabase();\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"token\");\n\n const [result] = await sdk.get({ token: req.body.token });\n if (!result)\n return res.status(403).json({\n error: true,\n message: \"Invalid Reset Request\"\n });\n\n const data = JSON.parse(result.data);\n const code = data.code;\n\n if (req.body.code != code)\n return res.status(403).json({\n error: true,\n message: \"Mismatch Reset Code\",\n validation: [{ field: \"code\", message: \"Mismatch Reset Code\" }]\n });\n\n sdk.setTable(\"user\");\n const [user] = await sdk.get({ id: result.user_id });\n\n if (!user)\n return res.status(403).json({\n error: true,\n message: \"User not found\"\n });\n\n const same = await PasswordService.compareHash(req.body.password, user.password);\n if (same)\n return res.status(403).json({\n error: true,\n message: \"Password is same as old password\",\n validation: [{ field: \"password\", message: \"Password is same as old password\" }]\n });\n\n const service = new AuthService();\n const resetResult = await service.reset(sdk, req.projectId, result, req.body.password);\n\n if (typeof resetResult == \"string\")\n return res.status(403).json({\n error: true,\n message: resetResult\n });\n\n sdk.setTable(\"user\");\n await sdk.updateWhere(\n {\n reset_pass: 0,\n update_at: sqlDateTimeFormat(new Date())\n },\n { id: result.user_id }\n );\n\n return res.status(200).json({\n error: false,\n message: \"Reset Password\"\n });\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n router.get('/v2/api/custom/ergo/s3proxy/:image', async function (req, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - S3 Proxy"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n A proxy wrapper for accessing s3 server.\n Used services: \n The code with comments is:\n ```\n // S3 Proxy\n const { image } = req.params;\n if (!image) return res.status(400).json({ error: true, message: \"image is required!\" });\n\n s3BaseUrl = config.s3_base_url || \"https://s3.us-west-1.amazonaws.com/com.mkdlab.images/baas/ergo\";\n\n const response = await fetch(`${s3BaseUrl}/${image}`);\n const data = await response.arrayBuffer();\n\n res.contentType(\"image/jpeg\");\n res.send(Buffer.from(data));\n ```\n "}]} +{"messages": [{"role": "system", "content": "You are a senior backend engineer working on the APIs written in expressJS. The project you are working on needs to follow certain formatting and conventions. The schema of the database is given below:\n ergo_review_hashtag\n-------------------\nName | Type\n\nid | int(11)\nreview_id | int(11)\nhashtag_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_trigger_rules\n------------------\nName | Type\n\nid | int(11)\nname | text\nevent_type | varchar(255)\ntrigger_type | varchar(255)\ncondition | text\ncondition_payload | text\nresult_true | text\nresult_payload | text\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\n\nergo_booking_addons\n-------------------\nName | Type\n\nid | int(11)\nbooking_id | int(11)\nproperty_add_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_device\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nuid | varchar(255)\nactive | tinyint(1)\ndisabled | tinyint(1)\nstatus | tinyint(1)\nlast_login_time | datetime\ndeleted_at | datetime\n\nergo_property_spaces_schedule_template\n--------------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nschedule_template_id | int(11)\ncustom_slots | longtext\ndeleted_at | datetime\n\nergo_stripe_payment_intent\n--------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_space_faq\n-----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\nquestion | text\nanswer | text\ndeleted_at | datetime\n\nergo_property_spaces_images\n---------------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nproperty_spaces_id | int(11)\nphoto_id | int(11)\nis_approved | tinyint(1)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_add_on\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nname | varchar(255)\ncost | float\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_notification\n-----------------\nName | Type\n\nid | int(11)\ncreate_at | date\nnotification_time | datetime\nupdate_at | datetime\nuser_id | int(11)\nactor_id | int(11)\naction_id | int(11)\nmessage | longtext\ntype | tinyint(1)\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_stripe_invoice\n-------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_order\n-----------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nprice_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\n\nergo_email\n----------\nName | Type\n\nid | int(11)\nslug | varchar(255)\nsubject | text\ntag | text\nhtml | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_product\n-------------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nstripe_id | varchar(255)\nobject | longtext\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_photo\n----------\nName | Type\n\nid | int(11)\nurl | text\ncaption | text\nuser_id | int(11)\nwidth | int(11)\nheight | int(11)\ntype | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_booking\n------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_space_id | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nstripe_payment_intent_id | int(11)\nbooked_unit | int(11)\npayment_method | varchar(255)\nstatus | int(11)\npayment_status | int(11)\nbooking_start_time | datetime\nbooking_end_time | datetime\nduration | int(11)\nqueued | tinyint(1)\ntax_rate | int(11)\ncommission_rate | int(11)\nnum_guests | int(11)\nreason | longtext\ndeleted_at | datetime\n\nergo_stripe_checkout\n--------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nstripe_id | varchar(255)\nobject | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_room\n---------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nother_user_id | int(11)\nbooking_id | int(11)\nchat_id | int(11)\nproperty_id | int(11)\nunread | int(11)\nis_archive | tinyint(1)\ncreate_at | date\nupdate_at | datetime\nuser_update_at | datetime\nother_user_update_at | datetime\ndeleted_at | datetime\n\nergo_settings\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nkey_name | varchar(255)\nkey_value | int(11)\noptional_data | longtext\ndeleted_at | datetime\n\nergo_property_spaces\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nspace_id | int(11)\nmax_capacity | int(11)\nmax_unit | int(11)\ncurrently_booked_unit | int(11)\ndescription | text\nrate | double\nadditional_guest_rate | double\ndefault_image_id | int(11)\navailability | tinyint(1)\nspace_status | tinyint(1)\ndraft_status | tinyint(1)\nsize | tinyint(1)\ntax | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_analytic_log\n-----------------\nName | Type\n\nid | bigint(20)\nuser_id | int(11)\nurl | text\npath | text\nhostname | text\nip | varchar(50)\nrole | varchar(50)\nbrowser | varchar(255)\ncountry | varchar(100)\ncreate_at | date\nupdate_at | datetime\n\nergo_images\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nuser_id | int(11)\nimage | text\ndeleted_at | datetime\n\nergo_trigger_type\n-----------------\nName | Type\n\nid | int(11)\nname | text\ncreate_at | date\nupdate_at | datetime\n\nergo_review\n-----------\nName | Type\n\nid | int(11)\ncustomer_id | int(11)\nhost_id | int(11)\nproperty_spaces_id | int(11)\nbooking_id | int(11)\nhost_rating | int(11)\ncustomer_rating | int(11)\nspace_rating | int(11)\ncomment | text\npost_date | datetime\nstatus | int(11)\ngiven_by | varchar(255)\nreceived_by | varchar(255)\nproperty_name | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_token\n----------\nName | Type\n\nid | int(11)\ntoken | text\ntype | int(11)\ndata | text\nuser_id | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nexpire_at | datetime\n\nergo_stripe_webhook\n-------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nidempotency_key | varchar(255)\ndescription | varchar(255)\nevent_type | varchar(255)\nresource_type | varchar(255)\nobject | longtext\nis_handled | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_permission\n---------------\nName | Type\n\nid | int(11)\nrole | varchar(255)\npermission | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_price\n-----------------\nName | Type\n\nid | int(11)\nname | varchar(255)\nproduct_id | int(11)\nstripe_id | varchar(255)\nis_usage_metered | tinyint(1)\nusage_limit | int(11)\nobject | longtext\namount | float\ntrial_days | int(11)\ntype | varchar(50)\nstatus | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_scheduling\n---------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nproperty_spaces_id | int(11)\nhost_id | int(11)\navailability | tinyint(1)\ndeleted_at | datetime\n\nergo_cms\n--------\nName | Type\n\nid | int(11)\npage | varchar(255)\ncontent_key | varchar(255)\ncontent_type | varchar(255)\ncontent_value | text\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_spaces\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ncategory | varchar(255)\nimage | text\nicon | text\nhas_sizes | int(11)\ndeleted_at | datetime\n\nergo_payout\n-----------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ninitiated_at | datetime\nhost_id | int(11)\ncustomer_id | int(11)\nproperty_space_id | int(11)\ntotal | float\ntax | float\ncommission | float\nbooking_id | int(11)\nstatus | int(11)\ndeleted_at | datetime\n\nergo_chat\n---------\nName | Type\n\nid | int(11)\nroom_id | int(11)\nunread | int(11)\nchat | longtext\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_amenity\n------------\nName | Type\n\nname | varchar(255)\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nspace_id | int(11)\ncreator_id | int(11)\ndeleted_at | datetime\n\nergo_user_property_spaces\n-------------------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\nproperty_spaces_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_property_spaces_amenitites\n-------------------------------\nName | Type\n\nid | int(11)\nproperty_spaces_id | int(11)\namenity_id | int(11)\ncreator_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_stripe_subscription\n------------------------\nName | Type\n\nid | int(11)\nstripe_id | varchar(255)\nprice_id | varchar(255)\nuser_id | int(11)\nobject | longtext\nstatus | varchar(50)\nis_lifetime | tinyint(1)\ncreate_at | date\nupdate_at | datetime\n\nergo_stripe_setting\n-------------------\nName | Type\n\nid | int(11)\nkey | varchar(50)\nvalue | mediumtext\ncreate_at | date\nupdate_at | datetime\n\nergo_property_add_on\n--------------------\nName | Type\n\nid | int(11)\nproperty_id | int(11)\nadd_on_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\nergo_payout_method\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\naccount_number | varchar(255)\nrouting_number | varchar(255)\naccount_name | varchar(255)\n\nergo_user\n---------\nName | Type\n\nid | int(11)\noauth | varchar(255)\nrole | varchar(255)\nfirst_name | varchar(255)\nlast_name | varchar(255)\nemail | varchar(255)\npassword | varchar(255)\ntype | int(11)\nverify | int(11)\nphone | varchar(255)\nis_photo_approved | tinyint(1)\nphoto | text\nrefer | varchar(255)\nstripe_uid | varchar(255)\npaypal_uid | varchar(255)\npayment_method_set | tinyint(1)\ntwo_factor_authentication | int(11)\nstatus | int(11)\ncreate_at | date\nupdate_at | datetime\nreset_pass | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification\n--------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntype | varchar(255)\nexpiry_date | date\nstatus | int(11)\nimage_front | text\nimage_back | text\nuser_id | int(11)\ndeleted_at | datetime\n\nergo_property\n-------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nstatus | int(11)\nverified | int(11)\nhost_id | int(11)\nname | varchar(255)\nrule | text\ndeleted_at | datetime\n\nergo_profile\n------------\nName | Type\n\nid | int(11)\nuser_id | int(11)\ncreate_at | date\nupdate_at | datetime\ndob | date\naddress_line_1 | varchar(255)\naddress_line_2 | varchar(255)\ncity | varchar(255)\ncountry | varchar(255)\nzip | varchar(255)\nabout | text\nsettings | longtext\ngetting_started | tinyint(1)\ndeleted_at | datetime\n\nergo_id_verification_image\n--------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nimage | text\nid_verification_id | int(11)\ndeleted_at | datetime\n\nergo_property_space_rule_template\n---------------------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\nhost_id | int(11)\ntemplate | longtext\ntemplate_name | varchar(255)\nspaces_id | int(11)\n\nergo_schedule_template\n----------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\ntemplate_name | varchar(255)\nhost_id | int(11)\nmon | int(11)\ntue | int(11)\nwed | int(11)\nthu | int(11)\nfri | int(11)\nsat | int(11)\nsun | int(11)\nslots | longtext\ndeleted_at | datetime\n\nergo_faq\n--------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nquestion | text\nanswer | text\nstatus | tinyint(1)\ndeleted_at | datetime\n\nergo_booking_queue\n------------------\nName | Type\n\nid | int(11)\ncreate_at | date\nupdate_at | datetime\nbooking_id | int(11)\ndeleted_at | datetime\n\nergo_hashtag\n------------\nName | Type\n\nid | int(11)\nname | varchar(255)\ncreate_at | date\nupdate_at | datetime\ndeleted_at | datetime\n\n\n To access the database, you have to use the below functions from req.sdk, which provides you a connection to the database, below is it's structure::\n \n const { Sequelize, QueryTypes } = require(\"sequelize\");\nconst { dbConnection } = require(\"./DbConnection\");\nconst { sqlDateFormat, sqlDateTimeFormat } = require(\"../services/UtilService\");\nconst ReadRecord = require('../baas/treeQL/actions/ReadRecord');\nconst ListRecord = require('../baas/treeQL/actions/ListRecord');\nconst QueryBuilder = require(\"./QueryBuilder\");\n\nmodule.exports = class BackendSDK {\n constructor() {\n this._dbConnection = dbConnection;\n this._database = \"\";\n this._table = \"\";\n this._projectId = \"\";\n this._secret = \"\";\n this.potentialInjectionKeywords = [\"union\", \"from\", \"`\", \"--\", \"++\", \";\"];\n }\n\n setDatabase(database) {\n this._database = database;\n return this._dbConnection;\n }\n\n getDatabase() {\n return this._dbConnection;\n }\n\n setTable(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n table(table) {\n this._table = this._database + \".\" + this._projectId + \"_\" + table;\n return this;\n }\n\n getTable() {\n return this._table;\n }\n\n setProjectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n projectId(projectId) {\n this._projectId = projectId;\n return this;\n }\n\n getProjectId() {\n return this._projectId;\n }\n\n setSecret(secret) {\n this._secret = secret;\n }\n\n getSecret() {\n return this._secret;\n }\n\n async getProjectDatabase(projectId = \"\") {\n const result = await this._dbConnection.query(\n ` SELECT SCHEMA_NAME AS db_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE 'baas_${projectId}'`,\n {\n raw: false\n }\n );\n\n if (result.length > 0) {\n return typeof result[1][0] !== \"undefined\" ? result[1][0].db_name : \"baas\";\n } else {\n return \"baas\";\n }\n }\n\n checkSQLInjection(sqlString) {\n sqlString = sqlString ?? \"\";\n\n for (let x of this.potentialInjectionKeywords) {\n if (sqlString.toLowerCase().includes(x)) throw new Error(\"Custom sql string validation error\");\n }\n }\n\n validateFieldNames(keyNames) {\n if (!keyNames) return;\n if (keyNames?.type == \"string\") keyNames = [keyNames];\n for (let keyName of keyNames) {\n keyName = keyName ?? \"\";\n\n if (keyName.search(/[^A-Z^a-z^0-9^_^.]/g) != -1) throw new Error(\"Key name invalid\");\n }\n }\n\n whereArrayToObject(where) {\n let ret = {};\n\n for (let x of where) {\n let [key, value] = x.split(\"=\");\n key = key.trim();\n value = value.trim();\n ret[key] = value;\n }\n\n return ret;\n }\n\n async join(table1, table2, joinId1, joinId2, selectStr, where) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" as \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" as \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (Array.isArray(where)) where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n // console.log(bind);\n\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinPaginate(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n const offset = page * limit;\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // join paginate with where as object\n async joinPaginateWhereObj(table1, table2, joinId1, joinId2, selectStr, where, page, limit, sortField, sortDirection) {\n try {\n if (selectStr.length < 1) {\n selectStr = \" * \";\n }\n\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.validateFieldNames([table1, table2, joinId1, joinId2]);\n\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n const offset = (page - 1) * limit;\n sql += ` LIMIT ${limit} OFFSET ${offset}`;\n\n console.log(\"sql\", sql);\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\" \");\n }\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async forceSync() {\n await this._dbConnection.sync({ force: true });\n }\n\n async joinPaginateStr(table1, table2, joinId1, joinId2, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n this.validateFieldNames([table1, table2, joinId1, joinId2, page, limit, orderBy, direction]);\n\n let sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Part: Join Multiple Tables\n removeProperties(result, properties) {\n result.forEach((el) => {\n properties.forEach((prop) => {\n delete el[prop];\n });\n });\n\n return result;\n }\n\n async joinMultiPaginateStr(tables, joinIds, selectStr, where, page, limit, orderBy, direction) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n\n this.checkSQLInjection(selectStr);\n this.validateFieldNames([...tables, ...joinIds, page, limit, orderBy, direction]);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT \" +\n selectStr +\n \" FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT \" + selectStr + \" FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n\n if (orderBy && direction) {\n sql += ` ORDER BY ${orderBy} ${direction}`;\n }\n\n const offset = page * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return this.removeProperties(result, [\"password\"]);\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinCount(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n\n where = this.whereArrayToObject(where);\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n countSql = countSql.split(\"WHERE\").join(\"\");\n }\n countSql += rows.join(\" AND \");\n\n const countResult = await this._dbConnection.query(countSql, {\n bind,\n type: QueryTypes.SELECT\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n //where in object\n async joinCountWhereObj(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n let countSql =\n \"SELECT count(*) as num FROM \" +\n namespaceTable1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n // table2 +\n \" ON \" +\n `${namespaceTable1}.${joinId1}` +\n \" = \" +\n `${namespaceTable2}.${joinId2}` +\n \" WHERE \";\n if (rows.length < 1) {\n rows = [\" 1 \"];\n }\n\n countSql += rows.join(\" AND \");\n console.log(\"countSql\", countSql);\n\n const countResult = await this._dbConnection.query(countSql, {\n type: QueryTypes.SELECT,\n bind\n });\n\n let total = 0;\n if (countResult.length > 0) {\n total = countResult[0].num;\n }\n\n return total;\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async get(where, selectStr = \"*\", orderBy = \"id\", direction = \"DESC\", customWhere = null) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n // console.log(\"*****Hello\", customWhere);\n this.checkSQLInjection(selectStr); //validation for sql injection\n this.checkSQLInjection(customWhere); //validation for sql injection\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`\\`${key}\\` = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n console.log(where);\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n if (customWhere) sql += \" and \" + customWhere + \" \";\n sql += ` ORDER BY \\`${orderBy}\\` ${direction}`;\n console.log(\"sql\", sql);\n console.log(\"bind\", bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginate(where, selectStr = \"*\", page, limit, order, direction, customWhere) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n if (count == 1) {\n sql += \" 1\";\n }\n\n sql += rows.join(\" AND \");\n\n // if (customWhere) sql += \" and \" + customWhere + \" \";\n\n if (order) {\n sql += ` order by ${order} ${direction ? direction : \"ASC\"} `;\n }\n\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async paginateStr(where, selectStr = \"*\", page, limit, order, direction) {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n\n sql += rows.join(\" AND \");\n const offset = (page - 1) * limit;\n\n sql += ` LIMIT ${offset} , ${limit}`;\n console.log(\"sql\", sql);\n\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n async joinCountStr(table1, table2, joinId1, joinId2, where) {\n try {\n const namespaceTable1 = this._database + \".\" + this._projectId + \"_\" + table1;\n const namespaceTable2 = this._database + \".\" + this._projectId + \"_\" + table2;\n\n let sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n table1 +\n \" INNER JOIN \" +\n namespaceTable2 +\n \" \" +\n table2 +\n \" ON \" +\n `${table1}.${joinId1}` +\n \" = \" +\n `${table2}.${joinId2}` +\n \" WHERE \";\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async joinMultiCountStr(tables, joinIds, where) {\n try {\n const namespaceTable1 = tables[0] ? this._database + \".\" + this._projectId + \"_\" + tables[0] : \"\";\n const namespaceTable2 = tables[1] ? this._database + \".\" + this._projectId + \"_\" + tables[1] : \"\";\n const namespaceTable3 = tables[2] ? this._database + \".\" + this._projectId + \"_\" + tables[2] : \"\";\n console.log(namespaceTable1);\n\n if (tables.length === 3 && (namespaceTable1 === \"\" || namespaceTable2 === \"\" || namespaceTable3 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n if (tables.length === 2 && (namespaceTable1 === \"\" || namespaceTable2 === \"\")) {\n throw new Error(\"Wrong parameter. Empty tables.\");\n }\n\n if (tables.length === 3) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" LEFT JOIN \" +\n namespaceTable3 +\n \" \" +\n // tables[2] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable3}.${joinIds[0]}` +\n \" WHERE \";\n } else if (tables.length === 2) {\n var sql =\n \"SELECT COUNT(*) as num FROM \" +\n namespaceTable1 +\n \" \" +\n // tables[0] +\n \" LEFT JOIN \" +\n namespaceTable2 +\n \" \" +\n // tables[1] +\n \" ON \" +\n `${namespaceTable1}.${joinIds[0]}` +\n \" = \" +\n `${namespaceTable2}.${joinIds[1]}` +\n \" WHERE \";\n } else if (tables.length === 1) {\n var sql = \"SELECT COUNT(*) as num FROM \" + namespaceTable1 + \" \" /* + tables[0] */ + \" WHERE \";\n } else {\n throw new Error(\"Wrong parameter. No table found.\");\n }\n\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async countStr(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n if (!Array.isArray(where)) {\n throw new Error(\"Wrong parameter. Expecting array\");\n }\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (rows.length == 0) {\n sql = sql.split(\"WHERE\").join(\"\");\n }\n sql += rows.join(\" AND \");\n\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async count(where) {\n try {\n let sql = \"SELECT COUNT(*) as num FROM \" + this._table + \" WHERE \";\n let rows = [];\n let count = 1;\n let bind = [];\n for (const key in where) {\n if (Object.hasOwnProperty.call(where, key)) {\n const element = where[key];\n this.validateFieldNames(key);\n rows.push(`${key} = $${count}`);\n count++;\n bind.push(element);\n }\n }\n if (count == 1) {\n sql += \" 1\";\n }\n sql += rows.join(\" AND \");\n console.log(sql);\n const result = await this._dbConnection.query(sql, {\n bind: bind,\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result[0].num;\n } else {\n return 0;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async getStr(where, selectStr = \"*\") {\n try {\n let sql = \"SELECT \" + selectStr + \" FROM \" + this._table + \" WHERE \";\n let rows = [];\n for (let i = 0; i < where.length; i++) {\n const element = where[i];\n rows.push(element);\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n // console.log('bind', bind);\n const result = await this._dbConnection.query(sql, {\n type: QueryTypes.SELECT\n });\n\n if (result.length > 0) {\n return result;\n } else {\n return [];\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async insert(payload) {\n try {\n console.log(payload);\n if (!payload.hasOwnProperty(\"create_at\")) payload[\"create_at\"] = sqlDateFormat(new Date());\n if (!payload.hasOwnProperty(\"update_at\")) payload[\"update_at\"] = sqlDateTimeFormat(new Date());\n const fields = Object.keys(payload);\n const questionMarks = [];\n for (let i = 0; i < fields.length; i++) {\n questionMarks.push(\"?\");\n }\n\n let sql = \"INSERT INTO \" + this._table + \" (\" + fields.join(\",\") + \") VALUES (\" + questionMarks.join(\",\") + \");\";\n let bind = [];\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n bind.push(element);\n }\n }\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n\n if (result.length == 2) {\n return result[0];\n } else {\n return null;\n }\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async batchInsert(columns, payload) {\n try {\n // iterate the payload and insert into the database\n for (let i = 0; i < payload.length; i++) {\n let sql = \"INSERT INTO \" + this._table + \" (\" + columns.join(\",\") + \") VALUES (\";\n let bind = [];\n for (let j = 0; j < columns.length; j++) {\n const element = columns[j];\n sql += \"?,\";\n bind.push(payload[i][element]);\n }\n sql = sql.slice(0, -1);\n sql += \");\";\n\n await this._dbConnection.query(sql, {\n replacements: bind,\n type: Sequelize.QueryTypes.INSERT\n });\n }\n return true;\n } catch (error) {\n console.log(\"INSERT ERROR\", error);\n throw new Error(error.message, { cause: error });\n }\n }\n\n async update(payload, id) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n sql += rows.join(\" , \");\n sql += \" WHERE id = \" + id;\n console.log(\"sql\", sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n // Point: Update Record by Condition\n async updateWhere(payload, condition) {\n try {\n let sql = \"UPDATE \" + this._table + \" SET \";\n let setRows = [];\n let bind = [];\n\n // Part: Constructing SET Clause\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n let element = payload[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"string\":\n element = element;\n break;\n case \"boolean\":\n element = element;\n break;\n\n case \"object\": // Note: Assuming JSON\n if (element !== null) element = `${JSON.stringify(element)}`;\n break;\n\n default:\n element = element;\n break;\n }\n\n this.validateFieldNames(key);\n // setRows.push(`${key} = ${element}`);\n setRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += setRows.join(\" , \");\n sql += \" WHERE \";\n\n // Part: Constructing WHERE Clause\n const whereRows = [];\n for (const key in condition) {\n if (Object.hasOwnProperty.call(condition, key)) {\n let element = condition[key];\n\n switch (typeof element) {\n case \"bigint\":\n break;\n case \"number\":\n break;\n case \"boolean\":\n element = element;\n break;\n\n default:\n element = element;\n break;\n }\n this.validateFieldNames(key);\n whereRows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += whereRows.join(\" AND \");\n\n const result = await this._dbConnection.query(sql, {\n replacements: bind,\n type: QueryTypes.UPDATE\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async delete(payload, id) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [\"id = ?\"];\n let bind = [id];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n this.validateFieldNames(key);\n const element = payload[key];\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async deleteWhere(payload) {\n try {\n let sql = \"DELETE FROM \" + this._table + \" WHERE \";\n let rows = [];\n let bind = [];\n\n for (const key in payload) {\n if (Object.hasOwnProperty.call(payload, key)) {\n const element = payload[key];\n this.validateFieldNames(key);\n rows.push(`${key} = ?`);\n bind.push(element);\n }\n }\n\n sql += rows.join(\" AND \");\n // console.log('sql', sql);\n const result = await this._dbConnection.query(sql, {\n replacements: bind\n });\n\n if (result) {\n return result;\n } else {\n return null;\n }\n } catch (error) {\n throw new Error(error.message);\n }\n }\n\n async rawQuery(sql) {\n // When using this function be aware of sql injection. Do a proper validation\n // To avoid, one best practice is to use parameterized query with binding.\n\n const result = await this._dbConnection.query(`use ${this._database}; ${sql}`, {\n raw: false\n });\n console.log(result.length);\n if (result.length > 0) {\n return result[1][1] ?? [];\n } else {\n return [];\n }\n }\n query(project = null) {\n project = project ?? this._projectId\n return new QueryBuilder(project)\n }\n subquery(project = null) {\n project = project ?? this._projectId\n return QueryBuilder.subquery(project)\n }\n\n async getOne(table, id, options = {}) {\n this._table = table;\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.params.id = id.toString();\n options.params.table = table;\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.projectId = this._projectId;\n const result = await ReadRecord.handle(options, this);\n return result;\n }\n\n async getList(table, options = {}) {\n this._table = table;\n options.projectId = this._projectId;\n\n options.params = {}; // ensure compatibility\n options.query = {}; // ensurecompatibility\n\n options.query.join = options.join ?? null ;\n options.query.filter = options.filter ?? null\n options.params.table = table;\n const result = await ListRecord.handle(options, this);\n return result;\n }\n\n whitelistField(data, keys) {\n let payload = {};\n keys.forEach((key) => {\n if (data[key]) {\n payload[key] = data[key];\n }\n });\n\n return payload;\n }\n\n blacklistField(data, keys) {\n keys.forEach((key) => {\n if (data[key]) {\n delete data[key];\n }\n });\n\n return data;\n }\n\n deepBlacklistField(data, keys) {\n const filterObject = (obj, key) => {\n for (var i in obj) {\n if (!obj.hasOwnProperty(i)) continue;\n if (i == key) {\n delete obj[key];\n } else if (typeof obj[i] == \"object\") {\n filterObject(obj[i], key);\n }\n }\n return obj;\n };\n let self = this;\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n data[i] = self.deepBlacklistField(data[i], keys);\n }\n } else {\n keys.forEach((key) => filterObject(data, key));\n }\n return data;\n }\n};\n\n \n The services you have access to are:\n Service Name\n Service Description: Service Description\n Constructor Params: Constructor Parameters\n Methods:\n Method Name\n Method Description: Method Description\n Method Params: Parameters\n Method Response: Return Type\n AuthService\n Service Description: This service is to authenticate user for different roles. Use this class to do any authentications.\n Constructor Params: \n Methods:\n login\n Method Description: This method is to do simple login\n Method Params: sdk, projectId, email, password, role\n Method Response: {\n user_id: exist[0].user_id,\n token: newToken,\n type: 2,\n data: JSON.stringify({ user_id, token: newToken }),\n status: 1,\n create_at: sqlDateFormat(new Date()),\n update_at: sqlDateTimeFormat(new Date()),\n expire_at: sqlDateTimeFormat(expireDate)\n }\n marketingLogin\n Method Description: Use this to do marketing login.\n Method Params: sdk, projectId, email, password, role\n Method Response: \n register\n Method Description: \n Method Params: sdk, projectId, email, role\n Method Response: \n registerMarketingUser\n Method Description: \n Method Params: sdk, projectId, email, password, role, verify = 0, first_name = \"\", last_name = \"\", photo = null, phone = null\n Method Response: \n googleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n appleLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n twitterLogin\n Method Description: \n Method Params: sdk, projectId, user, role\n Method Response: \n facebookLogin\n Method Description: \n Method Params: sdk, projectId, user, tokens, role, company_id\n Method Response: \n forgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileForgot\n Method Description: \n Method Params: sdk, projectId, email, userId\n Method Response: \n mobileVerify\n Method Description: \n Method Params: sdk, projectId, email\n Method Response: \n reset\n Method Description: \n Method Params: sdk, projectId, token, password\n Method Response: \n saveRefreshToken\n Method Description: \n Method Params: sdk, projectId, user_id, token, expireDate\n Method Response: \n checkRefreshToken\n Method Description: \n Method Params: sdk, projectId, userId, token\n Method Response: {...tokenTableResponse}\n updateRefreshToken\n Method Description: \n Method Params: sdk, projectId, oldToken, newToken, expireDate\n Method Response: \n \n The API you are working on has a below structure:\n app.post('/v3/api/ergo/slot-available', [ProjectMiddleware, ()>{ \ntry {\n} catch (error) { console.error(error); return res.status(500).json({ error: true, message: \"Something went wrong!\" }); }\n})\n You have to fill in the api structure with logical codes based on the users comments, these comments are made for the code segments.\n You have to provide a chain of thought for the code you are writing. Then you have to write the code with comments. While writing the code, \n embrace the code into ``` codes ``` in this format. **Don't provide the API structure part in the code, just provide the parts inside the try block.**\n \n\n\nWhile returning the final response, strictly follow the below convention:\n1. If the response is a list of objects, use the key list in the response.\n2. If the response is a single object, use the key model in the response.\n\nNote: Use one of the above, don't use them together\n\nAdditional parameter:\nerror: true/false // Required key\nmessage: \"Put your message if necessary\" // optional key\n"}, {"role": "user", "content": "The logic should be based on the below comments:\n - Check If Slot Available For The Given Day\n- Fetching Max Unit of a Property Space and Check Availability\n- Fetch All Slots For The Property Space\n- Fetch Already Booked Bookings For The Given Date\n- Check Custom/Regular Slots\n- Find Available Slots\n- Reject booking if host has no active regular time slots for the booking date\n- Find Available Slots"}, {"role": "assistant", "content": "To write the codes, I'll follow the below chain of thought: \n Check if property space available. Fetch slots of the property space. Check if there are any booked bookings. Look if slot is custom and find available slots for that custom timeframe. And if regular slot check if any scheduling for the day and find available slots withing the timeframe.\n Used services: \n The code with comments is:\n ```\n // Check If Slot Available For The Given Day\n const validationResult = await ValidationService.validateInputMethod(\n {\n date: \"required|date\",\n property_space_id: \"required|integer\"\n },\n undefined,\n req\n );\n if (validationResult.error) return res.status(400).json(validationResult);\n\n const { date, property_space_id } = req.body;\n\n // Fetching Max Unit of a Property Space and Check Availability\n const sdk = req.sdk;\n sdk.setProjectId(req.projectId);\n sdk.setTable(\"property_spaces\");\n const [propertySpace] = await sdk.get({\n id: property_space_id,\n availability: 1\n });\n\n if (!propertySpace)\n return res.status(403).json({\n error: true,\n message: \"Property space is not available!\"\n });\n\n // Fetch All Slots For The Property Space\n const [slots] = await sdk.rawQuery(\n `SELECT * FROM ergo_property_spaces_schedule_template LEFT JOIN ergo_schedule_template ON ergo_schedule_template.id = ergo_property_spaces_schedule_template.schedule_template_id WHERE property_spaces_id = ${property_space_id}`\n );\n\n const parsedCustomSlots = JSON.parse(slots.custom_slots);\n const parsedRegularSlots = JSON.parse(slots.slots);\n const dateFormatted = new Intl.DateTimeFormat(\"en-US\", {\n year: \"2-digit\",\n month: \"2-digit\",\n day: \"2-digit\"\n }).format(new Date(date));\n const dateISO = new Date(date).toISOString();\n\n if (!slots || (!parsedRegularSlots.length && !Object.keys(parsedCustomSlots).length))\n return res.status(400).json({ error: true, message: \"No slots for this property space!\" });\n\n // Fetch Already Booked Bookings For The Given Date\n const bookedBookings = await sdk.rawQuery(\n `SELECT * FROM ergo_booking WHERE (DATE(booking_start_time) = '${date}' OR DATE(booking_end_time) = '${date}') AND property_space_id = ${property_space_id} AND status IN (1, 2)`\n );\n\n // Check Custom/Regular Slots\n if (parsedCustomSlots[dateFormatted]) {\n // Find Available Slots\n const ranges = parsedCustomSlots[dateFormatted];\n ranges.forEach((range) => {\n range.start = `${dateISO.split(\"T\")[0]}T${range.start.split(\"T\")[1]}`;\n range.end = `${dateISO.split(\"T\")[0]}T${range.end.split(\"T\")[1]}`;\n });\n\n const hourlySlots = getTimeSlots(ranges, 60);\n const availableSlots = getAvailableTimeSlots(hourlySlots, bookedBookings);\n\n if (!availableSlots.length)\n return res.status(400).json({\n error: true,\n message: \"All slots are occupaid!\",\n allOccupied: true\n });\n } else {\n // Reject booking if host has no active regular time slots for the booking date\n const bookingDay = new Date(date).toDateString().slice(0, 3).toLowerCase();\n if (!slots[bookingDay]) return res.status(400).json({ error: true, message: \"No scheduling for this day.\" });\n\n // Find Available Slots\n const ranges = parsedRegularSlots;\n ranges.forEach((range) => {\n range.start = `${dateISO.split(\"T\")[0]}T${range.start.split(\"T\")[1]}`;\n range.end = `${dateISO.split(\"T\")[0]}T${range.end.split(\"T\")[1]}`;\n });\n\n const hourlySlots = getTimeSlots(ranges, 60);\n const availableSlots = getAvailableTimeSlots(hourlySlots, bookedBookings);\n\n if (!availableSlots.length)\n return res.status(400).json({\n error: true,\n message: \"All slots are occupaid.\",\n allOccupied: true\n });\n }\n\n return res.status(200).json({\n error: false,\n message: \"Slots available\",\n allOccupied: false\n });\n ```\n "}]} diff --git a/ai-finetuning/src/__init__.py b/ai-finetuning/src/__init__.py new file mode 100644 index 0000000..0ca5931 --- /dev/null +++ b/ai-finetuning/src/__init__.py @@ -0,0 +1 @@ +## __init__.py \ No newline at end of file diff --git a/ai-finetuning/src/finetuning.py b/ai-finetuning/src/finetuning.py new file mode 100644 index 0000000..dcd72a0 --- /dev/null +++ b/ai-finetuning/src/finetuning.py @@ -0,0 +1 @@ +## finetuning.py \ No newline at end of file diff --git a/ai-finetuning/src/inference.py b/ai-finetuning/src/inference.py new file mode 100644 index 0000000..92a6e12 --- /dev/null +++ b/ai-finetuning/src/inference.py @@ -0,0 +1 @@ +## inference.py \ No newline at end of file diff --git a/sample_flow_ai_design.png b/sample_flow_ai_design.png new file mode 100644 index 0000000000000000000000000000000000000000..7a11721ac745173b56c731fe6fc74f3b3eddaa0b GIT binary patch literal 1640054 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 ol6fBvoai64U0 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%0Af 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º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 Ez45 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 |Yp-w#{12+ev_R>>? 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%0DAWM^G~n6a}$&F%(cu3L1B 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%0A;^Ee}hg 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≥6_ zd`i}W^&tRi#@zta2GWH?l-k;8o63rc>iz`&x+n%4XQP8dpT94dIgj4MMusC3{(68u z>iev%XwP>;KkpNs)UZQ8)WXm#dw5B{eF;p`Te7-PhjND%4M-}aH(rvhJuc3s3*z+B zx_H-g`d9~X5ZMk%V_h8o7HMc6`1$ibqkf!CoFs2ay9e($D-Z9wX;Ar&K{WT(D#|VE z*5c>z$(#(RyAIhV6yA`teb>8~dQ#8M2mJYy*B8zXg&;#s$4YB-N_{d=u^3EkhNs%O zM>=3ta>XzdF5$4_2 z_~K%G4@!=TnT9PNGOTk)Wd2+LZ-Kt72%K0EDnNzWDJ-GM=L^oV4As-12A{nXQP=Z7 z96Wst-D;s)HW>y#RcgR@kMF^cTU!ERdtov{X5h^-q%hrDDk>KZpMv86)!Q zY1Wy^W>R>#UKD|r#2Dd>BPnt=tuQP7NK3p!tGqi@(YW^bwkUwrRT^F%Lne*`PY#mZ zZ3_n0UqdsTvI$TFaoA1Um(mZ|M0`MGd)B)!pNevX%%D!J{b*NV~HjAY}kAJS8 zKCL;AjPaD=&maCPGsBw8p;M6-ZI>SV^~nAcCTrynX~JP)Zq~OXtUV_c&bJdr#7wKm zvQkaEL-N=O!|hCgDeYA{Y3oN**iwv)g$pGk!_^NRw-~*O6}fHK(8#N99>x8B8*b}t7O zku~pg!=j>kwR8=;?H{GyGnudVzEWqg3eOE_ROm$gsoU9qE8A~TeE?dVl!Fz-x*jYh zH7nLL>GJ@46WaDF+$Tbqyktj4_`Z2HK2kMXvia>SE@+$S0|ybL(|>vb!;|0+28<#H zmI&7B&K+r(q}Y|H4*E3MgeK=Bf;cHgo~SRUNLT@3heN_UppR$>yW;D92bev?9W=#>H2@K96D|A z`)VEHGiLhehe|rG#fHDI-=GxrqQxEK^HftdOR_z!Z_LvgzZbmbnKN%loN~kgap2zw zyH6-l$P3+J_d{l=;>8@%o`87GK==+vkKOs#2W0|5H`&Qm;gHmgugb#qbKI}2*VV?o z@8`yT82g7Ig&x^6;?YU795p@zue9e4+C4{LXSFj;n?0{cvnJ%lyuI*ut3KBP7Liy5w{8opnoq-l-0jhOFdt z+3XO8YfpII^ayQ!LBb@as_NW8iDF{n9 zJ~H*bEMwKiF+rACt$T3paCFZwqG<0v{Ib~duy@Ix+3A>x#hZ;)hdOF*BuGNcFLMA; zzE1&sWAwizV6-8&M6Pv}Hy6x7qJul`za6c#4o3jujB>-3p&OWh+ydcmIpWBzyZxn5 z*vlZFS;J61GR272vwJWjv7dWpgx~{JC$h0tW&@Y94^^i4nVPes2md z6b{w7#SP4`gaei6{E5HyBuqRbzC0G5eW78^==?A_gnf+-sK@R*RL?MHn^`3g&iG*x`MB&W2x6GWzt?tO)p`l+$TZ=n`J1R-vQao$>JQ<9^_8 z`z`4ub0k7r@odphNCJodJ}sNM9a{is%%DEIf!+U;wWq#mob7CJoH;sW^~7tMIBOha zjlfG`Fvyp{YxLEIQuM`HWhq?RaMULqt^wVv@=IPY{b7mdJJoUQL)yVEw!lRmcRlf^ za=f}EYsaS&&G?>ivL;8!n(a&E z&Oc9=-11Ady>Ax;5ZQ5e6UAQ30(c1LkJ9}J2m{AI+Pit`xCa(*`V)Bfao9{{H8=Q7 z5JXV6n@$F1k~cEsXdMTHVsgu=JcO;XUsT-7H0F+lX-;~hy}gE0BKCBI5==y z_4~v>>{7_H!8(g+*K{BYyu)+b!p%|a0~KT~Pjce0HsQ zsDhi7CCi0nz70BNbN%$Zkaqq1=!=VG;U6O4aD4VsJ}b&Q2QlTB95t8g{8=^_A7kvA zO!h|q$}cSVj-@@>k#9Nr$C(OwFlH>bUM!x(%bj-8My zP#juyCmuY$oNANN^W_?(@-j>4yCa?sLvA*4Re-B<;noZxSR3wrvLU$~4c71(nxzGd21N14P!u z1ai+QwMTpzvm5Zi7VD&K5*Qj`)RymkIlzMl9)5p)GV=EqFk0UOkB(%9n&9bd`!wVc zBeDN$6;lA_()$lTNrWNZ!Tk8eROl=^>>Rf@O=Tf(Tcee?3~?3Qeu)SM`@y>QyxiO; z^wL4JfpZ!hIg_Ax)kKPt&pV{gNJAV^#q<%Ix)(*|L2UwWSAiv_>l#%7H$$lTgws(B zIenle>Se-5f9L68NZ#r1oJu#bX*B_){VXHfNe_k^m1{Iq-mA_WE^xO9f(GKW*Ms@y zc)AgfbM4jB=+`BW_57zupuXDn2+oyoHE7rH--DGVIdZeQSCwd*MSkHa&7;!$l71W1b0Nuu2jonKd_F-+S7 zJU84`+2GHJLz=2)!j@YfBieC*7i|PX0uyG!1OAwIe8Or6vwUC?#0V`-t%V%$?tm8~ z-o1RNJXHjnz;s&Qiu@c#O)S3Um>R!HgyEH8u7p^JmC0Y_uXQGYhTSH=oce`+?YsYA z#7BB0Wg9$pnokcSM-e+~Y@;Q>Sf3-D3f9AwiMh*_^o@}&Ci{u0!Z?8ukfl`ld{FG` z(;4(*xPyY|kRPFR4r@OD;E5ra1Su#quo_%7FcKl}qMGl~*nQYx4E^*#5GQaby6bsU zX*&ME{Y5~;%eb~R0u25x(b4n&lwfdkZ^U;PG>}P|?)38d{}X#qVJF!jPH)=GZ~liV zfz*wHXKZ=te;(atMx*}cmsreZ^zoNfA;sUkVEAt`jYwBNdH=5$fd7)lB;XF;=W;6q z0r{C+f3QOW9yPSvLEq7@dk15}|3VMt{kD#_#4oN>V86jS~HIu^1UQaOxsff7xbf65z`m9&osA zZ%8YjdA#1+??WQJs@g0{6s7f&=%v`Op;Z4Q_T--7F8hv~ACOu z?)(5g*L7pEHk}@_9FF6-W$OkNsEBhsgBYP* zNXW~1=F95##6IJRw0T+@U(Gmqp`w+G|ew(P&GbH%Ddy3vJs zv5S9D@}U1fIp+F(4bKs~XVGx~k$&prNP>y z^4ELkrD{iJh{8Wf0B)NYdCO9Sd#iGao^c1!j!Qfj`~G|I>Zjzc1af!^ELHxeNix}C zH`M4Wed?82JpHxIA@wlv>m8Q>Bq1c~aYke3KvYv))F^5i35@ZnNsv!!dtV=BlCNyo zq|$FE=1(TFTt!^`Cf784#iZDm3Z~-1lNJ-*i-6*>NJR=Tv%vv6pfVIR|lHVBC z{CnQw+Z4P60h9n8#}RZ}#0vA-PIC#xae*a4lvxoBeG8OZ`W&@u#4oypOeDQ|tJa@A zSmQED(qR^zfBy&&ZDlLp72pWwFu(nJT3tatbZieB*bR%08Gc}?u;kVbV4{F|{nlf@ z?f-TJUn=A*YAR$XhC1bp&dFEf)HUmw+F~v<3JYaKpK^(dEA;7D$Aa7>J#=Gg zr1^t+Uqq?ycL8pbRVR3&`3YheWJEI^F{b7GQ417AA$ZU0FS*W9m%Ft226<64WUXct zbuvLod&z7VYFEKY!Vp{0Ry-mzqZOM>8y%TOXK1NR*6Tln4?czqUmTt8-F{KhM-F-7 z>Aq*`Q&%mx||h^i-sm|H;T z?I+Hs9_`4vL2Edeo(+m_hD9t?&LpTs{$jSAgD`>fGCtP4CFDdfe4B*6K}Rnr_e-K; zu)B2AJX{Wz**=(Cc75?v&7}d})nG)>0{UBkvnr{50)|gW3%}^T)z`~Q;)CJ~8N2n3 z-luNpx|)$D3$HtUe?U2&1@eVC-6#Y63$XYp)`s~56p@X3zfGJdYjTiOK|E?Wb9Q&d zaP(VUIQkUf(d2u*w!#2iw~PwILRA*YYR{LHYXKkss2t&Xr9w(gnh!m2my3H>wQRi= zBXE*3$YOiwJJzM22JlvK8W;(vO0NWxVXos-(*AtkOGr`gsFpW0O4eLbC-hI;%0&sw-Lo?7^k?2Y?@j-V5vb{+~v>r zXRFoSd_XSx7@4ch$KJ7(!*4enlUUA%){#+8AAh{%uiB!ZHWphjEA<;jzfz4rSz@;_ z?XS-ox5ozCTUv23TSKOOjU*?!1Z*$6&ZcJ~u7AV$w~a0! zp55f@*NXenoK%+$jmPB(T0+y`CQAb_f1q?J_1|81Kbm`ZO#{Dd$&sfN0-DU=^Dt2J zT{f!2?=|3)_i|wuo47=+nGZf3uP-DEWVEcb*%ozY9BxFjEzvs;`yiu@e5^6i zP{`P;#@XK2BaGn40SmN*fbkdhG;dRSO0{0%9AvuqNrxohQ!OY!D9l;c43{R@M0!W2w9eElVL3Qg> zP&GQ@7XdD^1pZFt^cphDY&EZ)G{M}-rmnIb8dQ+I{?=Cs3uFuFi3xDOh)npoCn(gg zOH%Ytm4y*bvM_}*cROfvr8SMaQcIO_)+ij3z`YWbJJ@zy^GBeu0D< z{0-GD`Sf6*doTLSEhN{?#w2QbwH@5j4Aq|2VjZpjKkaGvAMM!&oL=7eZ|&J%9~=Q@ z%dxYv`_5v@lH`X*4a2GWevn>=iT$r4jXEbyMYzfc?N0Vkhp+Fh#z+tezJORt)MM?o zvB5d}VYwtZF595qJc2pw2nM&h+61ZsRc?MQ+?0yF>IVjG1w5+`OHb5y7s&=N1-(v~ zgpywLA(71obeUsXPiIi{$liO^eozb!h6vy7<4Vph_GH9?vw0n*2@WcGXvO&cXB|93 z>L*P7Nx3hq67d$O_ftHA0|E^TnS`I4SCud*8}nOU6$AsVE)vCTbGn0wh3)>GemtN0=V-iadnn7H_! z8gQuO{=&j`OOS3Zovk)T*(3NRrT2qMD9=`Y2O#-8Cd+Y)(w|OF2fU6#%NC+>w%~;^ zEIOdfY@VIh107@kYqFbsEV0{{^B;8iDeDyKJ2~asiXrRc4&v-E$!tB(AA@gc=CKrX zZc@^OE4NUr*-9m$O=;=tj$JhL&eIQjMjakf!tS5yM6GySuKBjm>J%l?4I->2ZW$e?gh!Cje=PrP^+%CIrt_Bj}H{$DU;GQ3V9A)Hadwx*= zaT+nwu?H)cDzp`>cC6#F6Sql_udoO5@A7iNQlx~Pc4yTlKlxqqYOr#{c_99#AL>v| zni6&zQ4QSlP}x;$FZuJed7NzIoyOr&YQ~-cqGg*u?npL z=E)`ut3>W4N}BmGB}2{@+KmFwiH6%sSEh-ozet2U8O*rgLcbr171o9YYnm%v;MX_9 zY1#{*lg|V&vyz349D8#nWv+6bE>)Lj9ssMi(6L>ktYxXnMuTl4yID+HQ1y2-i=DPa zHEShA_%9~5+iK+wYXf|Rt@$cZWQsn}0A?s5xBdBql+n*k=xry8<+?oyu)o4z37KE? zcZ!U=d~LDse=;o45C_{1L}ECzT7SolkOs}+uUg zl0N%lG<;YVzkjYV5>i@XOru6zbs-v5L2}uAk#Q!VD>A}Qvg-cayI7HtF*q@vW`~U| ztmYsm2ZgV8V%jBFr=qHPoGhpOV#&5+_c)%nT5LCLu`iGLfSs_r9|55IttkjhHwkQa z@7_g?0MV)Cg$7@Vj=n8P5x|V=p$g$N6^H587?LFDqT=T3WQlYrr8BXwv`Wvud_f2k zcrt?)mfNPwB=O6@uN+RS86&PQ!*;&MuH{JBgkP^W+eJ+JV@uH=_nL%f2@ePk zAwkjMJQaY<>;dQ)b!y5A;PZW00ySEH({fjlNl1tW`PVeVfA1!pEEhSQ>iu$m8vYNZ zkw4Xj4|}6JijAZqq%un+!IT2PESVDAgq*|4R0Q{%Q>l1C4fnh0p{Qi(nH2W~V<7pq z(=*z=+#i@~xA`*2GXQUcmRMbgt8HQK&ogOj?4oFjJ{nBqcF{V_wpoj4f9{{1DmTgF z6;`-jjfVS$tmCv$_-{;&-VIm2pW(jE)W;L2^GTo=*e)pHbACk#iEudD%J>l%jM?`R ziE+v_I0z=~({mxoJQO(8C=M>bP9yX&mgMhb#UtQHg~{OV6|^TCGC}VACd*~N=NDvU zbZYL4+0S1^HbRnGJ`*g0ST_I4la&550`3SmYscd-Q1)v0;H(iIhO?BD_Dam|Ng=HG z?megXTG==3-JPm8m$}LD{92BJXfcy`Q&Aky7owa~r#n3~d@hjsbWUCCYxQA;3F=BDacGRZ;d`Tv9~;-2Yh- zwv%cGb|kvfUg1Tz+%dG{sHQPAfDD& zdY=k-`UEk0UJ0jQFl->}X6{pzOhGw-x@H6BW7dXi>r9);^b^<|*0jGieWP$#BX8t+ zM(Y2Ch+4cgs)+;PmcBQ-Ad9_cSGu~fVrphhFog-1+lGv!Gof9leaH4FFC@;w5U;)3 zz1Gf57`psVswI5ozJZrfRNUn!w8XjK-LWi*)*%ZOa;1gP?d9>f^ODE=6-y%*npLR4 zMkid?egyCy5=r$Dx7Y~{TG7w7#l_vCEj0kK z<_PN)q@ItXW}h2Z_FPE5ZM4-dmK%K{W~Xou<8UJ$9Qvl~KsY_oj`qIXyYT}UCuu=~ z)&Oi&m|ONsRd0-HKMK7v_o&+L40DdejdYUpMw1wO2wYS%`k>5 zQNR5$RHH{5Wvt;KDJ7KwA5q|@JtMUD=~0W#ESA(lK1167LLmY zd=nFMMnlSj)0SWJI$6=uy72L+k8MQ$o#r(wsi(tb9onNcCdQSiy8K)9rEo`DS3h2b zUiGKZ&^zr)Fs;AW^fMp5Cs32kiDve5ZRDXHOcttAfxR)yFe!+2<03~&*d_!t82Bu@ zp8q;VI^9R%33B9SXUf%G9(={ZI-Atbd3NdKWD^(IyryA;Sdxapatr6-IJ<0oeOzWt za8(34yLavxMG2}S+pjhpWToYuY^rcr-<_>$g?NIxe*+TWKo9~{PS>e%agW;wpH96= zE`G1=tc2(On%t>3k_Ubpw`D!WvTec$bb8FObXS(IEwk6a&Y^AL=n*R+0mpKri|0+@ zR#b}}DX|by{(PlSh#dQ-y5S_wbf_NTi6ti^*qda62e%PT-%Yd>EY*~DzhfA4=f=FP zO{X;r(^9-O!|M{b7aeSsFLikLHa+zQy`No&jGeMCv!1HFk5#1M)%!UayK<+?PQlw7 zn6`^YAFAXeNd_||t)?YjAtY%QT)B1KI^$VUR-Wpso4oDoNT;0}Zh|w7nJt-nH{BjA z#~rzj_7y1}|0YXTM}AZ>?btC<1&bT-??T05WlILRnBOf0G?v6u4 zzi@$oA}?RES(e`2fS@?++}b3AvJPb#V|vapj_}hk!}o!e0BRPg7Med}4H!t4hoSKe zzDg-10xq(urnaX)V>ATYS>l|VxLEsw2BSWd-qJlOFdJx|XtPqCXK5$rIoK(l$7Cd0 zj3T*RB1>2q!^!=e4*^TO?-Ai~xrA?TTz^`RbkeGYQ05q)y@>filPFJarVn)l|9jDY zB&c%^oNG&H!C!{`-z#DrmSCD~130og4OOP(LCi_vlZVGfyRE&Nsd@3OUEic-E3F6w*PFOEa;{j9!x2Wbe0wOptB+)ieL0!1r7aXMe=q zw?kXNw61@<40=EH$+KOs`9@O7K>N$TFBHKQ8bR{C7-)Ot*b1OK#Ub>=urUJ!O&+}?(%6=l5!lu z7?tLB+BEz~kYjA+TH zvxyJ8#@k#hA_6SSG~v!{J3P;gczM6Viu z-M-hOxkJD(Afe1|Z2K{B8+Zf9t!~`yachL6qa4mgBB2|eN+5ac>&(Xuy!dUC{-?>p ziCzY4W~$&BH88;xK8czCxE{kKKPHvsC<`FSmJRVbgQc|134ePE{>+{5)Igh>{eF!YV!TgmM$)lAclcy~A+q4ajEaDg1j-W?x~OaGV2Y zjI*rl7mqkTc6xkna9b|dB9xeA;J!CpYcfXwAX!SND$7e-x7}oY zc0}NuPkeK%>c1l~WnjlPDJizLHOu64s*n53hj1PP*SR45eW?bOoI&GD4Q0-IDk{lh zmA+9U#{wq20^w^Xa=|c4dpuz~l%& zC{Y?#pslsi>iP$j)8ZMcY~iErQUiWL;nz{hZWKikKkn!rlI@CLEFYzq3nD{XOS#K{ zNP(W8M>+?j2#*E=^D2zow_m`P;tx$ZJ>P~!guyiC%IZCPmUhDQ6hy({s78N#2oVc* zUcR2cwxcLoAST_LVq}oi@S&Rw{JsHvAkd?cY>ADd4T+ zJsb3K-L;OUBK*C-B{aWmVv}3LC|q(7uDSRKwK@&O6EbUYz$M}*aM7T#jJ*uo1a0-Y zg1W5O6)_wX^5_8~RF4NPtW@?UmPFg=7+7SVFgCIfnyS!lDej z61^Kh*2U%BuRngriF8_M;R#O?q<@qnbDPTIc{Zb16o3B_>;>JM>cEE+gG$Zjl=A&v zE-Z)Qk(f8-VO|BUpl`%5DN`gbkc)8suXsQU2JI|79s}urVo}IcEYsmX^yotWb@_Ux zm!$J*B$rgLw@ehu#9Dp=1jLMCTW`R5$cT=J)U_?3R&>4O#V&2+6Q7i$D+K2-HXDj> zhnwgK&SA1v@P1!Jb_+khnUf zah8$RtPKs_(BtKidlnt?2wsu!v+XlTb{}SXv%{av_64(6vY;0n(R(b{A^hCgx4Z-* z^n{*(5!GgVgj(-&{y`Q4#391(mC+M&?@#iwk`&D}XUl&*n2tBvxyUC<1cM$A z(yfAD62H;f!WLHjr%YU@jhur+LdH?n!njZ78)+S@)V*G1XvWoL zX5A>}7lJD)$PMa9Z-;Wb!SRCVkFhp5F$5{lwDqEc0S&P^zFdK*5o)(Q2|_|054Pb* zG%LGo{(!jt1}s;QKp>(y@uPgZrY3rRL-QU@jcg9dil_(kagtdP0!j{ldqho}%R zLxFhEsQ;ybOAN7doN8nnYy`=X$+4#|`p{Kw%{)GfbKpdl$uUs10u+4DFO=9aQ!#L#IWOp|4LBV)4cH90&pirms-JFb|yx|_Tf`yePNc3b*tHQ6E zbta+I1ZWC0hlx^?^NN(VF?C36(lGiGJ;v;F%%(ZD|9Do~gwlj;^BOFyHxh||096fl30>%JGP*kSa3hXTr`%_W3P~q z2S0>x*+H4)gM`cQl22NAIDDqSDUynRNl4(wQbT2n+q}RNg?Izz=zjjuv}llfiWoo7 zXVDispIfIOH1EHViyo7=#}TpPOPit;OxiJN2?uP#Bc105GIFQaQp_29%KK1jZSzHS$7_?8UK=J@wOGS0u>}P%J#xu7bHHr*CRZK9nRg&f%W2rq@ zjQ9PbcL%!0eaqbef51-nQcyjM;{A2lS&Kj992~P?N_GRS&Y8$QqRO1)Q8)8{qRYob zd+eB7NSzOw|G!=UWZIJcw<|HSfU5cvX3@1znIi|UvqveBd6Y={p2$`QXqOL$15a1_ zF&rp%IIKfSzuDR917!NSL2%QcI#qEo*v!OC9xjF7PhF@@S5owU0$-m|PZsp4+DJd2 z_daDss5Kr@B)v7kfq)e{aHbk6mb?i%!EfA+p|G%~!31RfQ{g4fZxrq;5Ub%yvVB5J zCo*Wg&+WxOT=5AMcowpSxT4A1G4!7stx{2*YqrqiRg-&I-oiaaItG|JG@+^dz^th0 z^CWZeo0LJ&3m)rv}qGY#BiA4m=h*(>7*l@ z-pf)oo3imS;uo67K|v@ZTM)CT53xfp_s}3 z@P!H6U)4Ur9u;Ql$Nx%87b~^2#z)c-H%6=2Vfk%-C=-d{Cjk}gO-S_x_MEHcN43+d zwzdELZQh3GmEvtrWwh=+yIW6+Z(>=Sue8y%_d=gtG)7QPXNAc8eua6hU8D$yTizdA zu$&)c2yXui({%XTTR-P4Gzks~PmmJo`)NyHO78Qh?3bOy+bmqA!6qp?v|J2J zg8MA@O7@CJ1o*OZLRkpEV(j`0g4}8{S zrIcRpl{_zhzX)N^o?c&78nj@2Sg!?FpRb5pok?$&eyv5xYo&MB|_Oso_gT58CVz5h_!w#`Y`oPSfNu^W8s|tChP@0S9XD83~}wVMlnt>bdixe2Db7Y;$BsATo$zT&;RL>Z*?@P4_-%twZO~~v$;ro z{*4UNhztG$6X5f_mt)ctS%$XrR9mOVZ9D&PqU3e5s1ea$$R3o1A%U=#5BsH=gK#%z zn|u+u{O72J=h=!x;uFeGH$q8-Hm}Rg;QMy$+!|o&9Sy(BL7J}QepSRF9@{p>f+rzi z@P8ywqr$35N1JI`5y=66`Q%&t(87W@saC7~FKzm>lAU%nB|+$p9rz(r_gZAcX<N4v2#2Oe{Gkmm@!$yXxFymqYh%-Kjn)w8540Bc$ipo!dntx>tf}f_S8{F|3NK>P; z06mPdNd%_diYR#Jt~EO^ozGVx_cK`U?0WzFFJVngvSYUmkG23fF*NE8Aa8A?Qvt)P zTJ7=_RMlKGvR`kaKx^$477>g+MJG5_7QnZ zSp|UJ9vEiQDuC$b{@vI;c50y9vUd+R$fP|lR?FnM8%tM>R@tc?=I8GnBpYxgD)4qH z_x2alJ{8bu)KtFXSjHA+K@P#r-*{xO^MdT8e;+AlhGV8|P3wp?Wse28<$M#yVB6r~ z)o>>2ZityDpKehKn9(X`u#@)W%5F2KXXMO;5#y99q`V5GYf8J{6Mcu3G_1}N(0Sn5$k`qfLGnfP( zuXfURrYpQm3Y{8gG)Fn4|ChcAZWfo&Bd5+u^m4_QLx>zhEmXbF`2P-ao*Y7ox87Cf zlkwkR;rltzCk|#0n z%EkvwgI_ZN0Xlnr2Im=GJGv{lC3o*;r^uUL5gfzQE81wLa7k$IyRo z5tqO(6y2#DsjINMhH#tidl(vsH|P|`9tD$ z`}=WezOX^RpTi>`6?b>~fkp63&^rVzZ#^XwV!QcL;4BOgyU(=L?NkIh{@aI(ZufqW znKlX>xC7d1^1k<<4xN6PJ;=1l`}H1puiv{+_wM~Ua|46@qGKmd#P6q&RsKXqrMMN* zAaAVu%W>FmiV3UutV!6tfy(WjZjUuZ&~NfNu|U(k)m?M>x#%Xn>xvBl9(fJd@e_X z(*;T6aLKs^fqPbv0vuF+sY!z3kQubKVX$=;Xw#3jio-B-)YNmG z-%NX-Fh0Rf%LTMJ{=-bN`C$#BW|soEQkYh0BEOvgTj@-n*2a0N^!n`c_x@Oxr?}oN zz4ELdw^R!yhxibNV|bo+5hh;Vp9_&QCH-laojN%ohwlBJ!f#vvQpxy;hX!rf&Trwi zbutZzm1)roAq6hcgg;HKcjbF^Q~<)w8WN#8mB32pejLkv`^}uK)}d=GNjy^_*lE7; zXN=Q7VEoSjh`TPtl|6&or6TSVAIhqskonaqCED!uj9km~ba02DVPHEPXy6SxU@wS! zSSOqDW>Nt%zA1&OEx}Z=>hYT_*P4{76D{S8|74)4|D!m{0MC;T!z?p$H~52_#1Ewg zC~GMYA6AW%)%5xQR6{9Agj>0y zWCf^iwzPttAfOStT2x3I$G5^;9E8ROnbIxHBF2L{>dM6Pb6(}R39wl^sxSrt737sK z?!6!;ytdjH3r*=5a`GL2*2PWt1&qJj_0Bt#k1MC!U^1!C6O3ik_jR!=?RIo0LI~UM zU#89`Eb2vt2^+D%tCt&3sLUUrM=al~Sk={&lg!~K&DC_Y`cDo0+YhsTf73;H-1EEh zV|83A*D`QyyY|fq3hM;l`(e9O&^&B_?Se1ww8_68PG+KR0c*F!YkJ;fAAjrof!`Lm zmk9@#Kl9tKu;YJh1Lbx65k6$7TA*G@M_X}NYf5{2MJ($DaM3_#F{zp^&ob1;Dx(h4 z?q|&47apke6jS>;fFMJvH%wnQ9LzH_3m+9OP*!`V5#4EFIS0QtoF^%_yNo6(m7ae%fi`^ zU0hhkrj2OEn|jaj|)!VC0;!CHGwys$^MA+(QsL0(L(^(1ER~B zfrOpvpT$C?4jq51T~FhYq-ec)4l5o?{n+RsUKN* zP1nEvujF+IlJQYK_5a$B-odD#Y*5S$!3ghirX)SikyEB$DB0n?X`2S>b2hiX>h2Oi zSc}D)Sh;lPB4~gm!in)iJX_%t;THI=E7>mQDg@jdq}em;V=&YmSLpH_V+ggf-Z7g< z~Pram0@_gLYgl>cvn^tWt|fWO1=1eU~z*CN)zeC5i>;kf8n+C&EO zh_tCKmco&=3KRWoP}Rx;q&(zMZ~0~&Vr@=k!trg(6A3mMWzg%yJtB@oV7KaINharr z9(gT88mojiWcy`n%ParoSj{FDiRel*m7n5Ry+H3L^bc2rn&f;>+d#qvd@mYHMEA}2 zl!ecangx4sJZp02?Wl91iEqna>g{gXK%o0JPV99gmW_egu~)Y?O#LcEnP4vj7C(j% z+t>!y+~XtbvL4i4Lk?~ zg`9d=YJ1+)X10jnhTy+ijXyeLO%F}Byh11D18BJNAX6n9rIa8f6Ae)f^hoJ@rhe7p zdf`9JyX8G_m#1^sHMGJ}+i%IX6)2?mk$fzRK`EUz%`VdTyj{d1Zn?@RT}6H%NXnSc zSgcl(*6rqJ`6_P5qk=>o%h^QdF4o1{UG0HWkRP`gYy1cYi9yQDSs}-+4cZT9I>}Q! zF4K9-WsZL81$QTjEpPG}e^Z4T-Q1#u3+> z!jZK$Txs8BCu>n*)Fm~Yg!-Z@Z}#WTb) zNOapje3eODjXue_I|M-ur%aZe7RdyWy)n!RNfUied8nQR$vL3R$n&`AleThOCpEcS z=s6}_?+cyk{f9v01h6pO916}G8Tg-f=G}ndafoUaRn9)Al{t@vmFB!zuiR+&kI4%= zm}6YmLaB)=8v_6~`=0*6cpCSD|JOB?OhGHe;KF~L-?nw;%Ur0$#V6R4E@q!`$DI&o zyFMU>g$0A5#-{Mel7+c$T-SddQUEXzNjnIDJw-qgRPJf&9*SbElFh4P_PY}40XPN+ znYJ3m7V}*sTA`7xAr+K|*oCU0KT|5FMW5x@*VFtSY`}y&QZA1UY4W^EsAbgLriB~nNb43`>c z$m>);lhZrn)yUDP|G{ZFNM({i(o+4x_{G<3IjU;_-< zM!y^Iiv_V|r|)H>e0@}Lot7bYcx$=n7ye4Fp0g#Cr21EIr<_i6PB(4PJ7RMAs8R8! z>9@;vCe;Tyak9StN#@AjgWw4EoP^?w5zwZq_=gMIUtc;CKYR#Au7fqbSeJ)3d)%VL zK38z%3B|5*>7(s${Kwtx1lpuN4 zT)q=Mfz3*fbbo2l=A3SeZ+maa(<0?#-Or}MUWwBjAm{ucf{3X2S8I{?m@BiT?>hK`N=iyP zI55xGljEve387!`Ja=Jf0xlfY%`HoOp@Gv!Al1Ybl&`gecCq~GZI9~6;l~_s0;M*O zYFKWPavchtkJSAAUclPs{M+K`=0I>PF@Y7vrEKC4^L9{7;n_eteU=Vc;7wXU?a!&J zcCq&tCBW6vK}oig^r!Epf$GYKlzy+#I8@q11vWFrjF{@(hMj9qkJsGHjMKg5E3Nb0 z0YdFhhuOD$_gaSgWN%aQxlHIsLHGraG$*I6A&Ki;E z@VZJD&0gqKewk48$NVf9JwxH(mdSa&W<8Cx*5rt?4jgHho0udP+kVDOCKc&m3Wn|z z5%mGrOq*2cCYusjybgupqGC|C(r-4^zDl;97gW&Kh`(D^o@@!CX`Yai(^{7*6<;;= z@<#4MW00@m_!F-61N}F&eUxcFT#v>)AjDL~2O8!2U5$Pzl64~G2#u~x&(nSMB*)eqd8?p>}2?p~RbTMr!ugjPjk3Qhp zTy488#Rj1qL)TI}TMK~$Yfo3q+PygtK}v0g4pEE#Yl-WeD z_j0Sm)*Nr5)Ng3+=j2 zccOsDE@MxCo{!InK&raMZWy6Qjo}+EpxlHx5lW;I)y&XAnVzvZ8f(L|4r~m1lWzaf zSL8P>0J*x)RbSLT7MAe#ik89`$75-JcH)JWYJ;%6y_R*y)Y7~_+9O`4uBVdDh`1?Z z=(wN10I$-{dJ)fVc}1=geV~Ki3gJ{bPw^b=$PEhe3DtU3K` zIs)6AHX{Jdl%oA-P2pzvnr<9OtT0-$^cvSK0qu%0j3L?+_sxbFggSw3Oc|O)newD4Q2_ArS8YW~8E^77vNT9+gix-|mpA6Agd80zoO; z$hq+JB{o+l6mjvolqleBiLlV+RE3DGK#jibilX)=J-vK? zL`oU7B6Qek*%+y;!^(=X4E)VcykMgC_U8BWdKpAm_xbm;-F^G{x={(osc}Rj{+ zHof5WqXo2r70g85;zheM7fP@*x`2@$m7BWdW1u+;JZRG67b_*`5N{QOE)jcQxQJE1 zxJiO1M<=%bEFc#P#sf;q=wa7s7W=qR>9zSeTwZBSW42LO`%^fA!7x7glJ3x<>QS=j zP(@e1EYX>41|No;$AL`&&UjFT67{h)W7hq7Z5u-fc@u z{v(Ru{AP_%rm2}QUeNbitom=Q!R`TOwbLpamJ5&S=wD^)vhPdVb=?>P^kA5<_qQP6 zId-ff&CYT+{CX7I!KJ&-GZ4VWiDMKh4btmC8T2@Rw{-izOtGamDmfr0hG-BW3sA?M~Keh zL8RgL{iPR;cCD{1!HWKoGs@w@P6FE}v~T6`+f8@tsq_;IlPo!T)`D>U#dBo$Ot(s9deuS$z}(1Z!KGvJ##C;oGE|lvYK6z#64OR>0`>9A;54{vDEBTH;WD-lLR*+<$I@S~9d{Om6fPD<(6! zkao59V0Ky~dY5^wj3yYXu3z`dZ>FN1{zyUYGtkgemMVeb{C0Vy>HYdu>ub%Ge^3af z^xg*t;T3`(L+*b+l2Q1^m}(dX1NA|SILT!}`oYiGIic)dx_mt>7h5fyb8RM?fr8F& z6*_i!XC^K7#Qvaa061ImGjoX+KeLF43Kx&2Q7YPtFdYI0ue~0du8D|tEfPo@Poh9? zK-MsIv&&BcxuCfvmJ++gaD!l!E2p;L|1kB|QBj6n`!?O(NOw0#x0E!}N|#7?4GkjQ zB^`=Nic1!^JHf% ztPe<7=tU&rzk@26p^FS`cx4cIS$DLXGs;WMacaB}gbmJ5!+o0pd<6(OoiBFVb+Z2I zf7Lyf$5>SyI>jk|o>pNFfS`#txs{bOLo;V=?pd~M<81Rc)qk1xv89Thi&mL$4!hR^ka!3sbS@(%|=_Ikn9Z_z$N} zDej=%2>xPdLscn1;-L}29xt$0olk6S6nu-6Ho+Rq(0R5SPZ}cF(iH2XYvC`DLIYvT zr^9N$v8crGex{-?3SDr0JQ`Pr>y2TM-*)=L%y!1cqq6t%hW?pq)x9T4t(fBqA$&oC&04jW^ICC@wM|%t;uKK4Um`d9ywbBW^?(# zH>4T14Jb?!WVHbrsAzT%(G5*=x&J?dL>21$e;54w|DziH`>lALhk$5&AZR--EatzR z5i$2HG?;IDICMWzs=xAGEY-&W4dRR^pkH$S+g3BTM$dlY?9!#)8BZ6*ECL|QaX%3C zO9_5WSqdUmI7(>TmJEKj-nRQ#0 zi^c`*xaqt^Vj`(RX~>)lxmgJXKLG2~C9tP0}e#&ctwtfrb7L!=W##eEHSrlA^{noK#nHaT=HT-a` zO`8rb`ii0bHCyjBkl~rTX#TF6px%vJ`;!ndm1&9AfpuR9cs;%ke&VoInGK77z*LKf z2^Er}pMjh#JlD~|%J00qQpXnu1a=GjmT3vDX$7}G1Y9-Fd77aj9}B&aineHf4R`>z zci*}ygKn>0H1s+|#Ihhu%9;3TG3(=IRjtK`7{D=_sM{xpCLP z0J1M%{8m0-Rp#jzNZJpZt+PTFqd4-`DOc2@_^K4o`d*#14!r_l!%ao`{yRc)lZ|`T zcS_+mh`2P;fg0CLTkregNK>wUWk;w^S3uH&gKkR9ck}~TIDeG)NJiZTv{wT2)U)YP z%4rrpeivL#9wS*g&;+NaP_!UYAv? zRjSa+Mz;Krx{zAwx zc&R1S@ZxqxtOKWqJ&~P}st&e4Z+w@G zFu|A~qa6x5#Zg^1KiWXS&%5sfIdG<7suwqp;%Lk+ja(Lf<>_22Siy0vcEQyL6^E)+ zcYPAnVCXwbIuDPtUR2&ao95~!5^Ys}%@ z8@;#?HgL}%^w2nmhx!R26hDUHQzvHbW-d#i`@^@%GQ_52rR%E_z#VKUO{P@e`E*A` zDeBvP7bQjeIIVX!0vNdyxsPMY$>e-^C%Ah{Wj8=b=lV^RNn5O4+L{qXIcc8ng16I* z5v0)we=rTkxvW3%8mCmoUeubmX*IM@*Ot{Db-@!GjnvfCQUTM{2m|-59qI`-iPeqM z%RpXV3YJdC>8}S*?=-3anY+*d(0#DxryWjbt1II~C!cM|;529PT+D_O+?SPeRR#hP zpGBMotDBdjs$hQ`=NLe;*1Z_F(*_1-ap38-J*h1*(CYU$h#R37i4iB@yT2|u=)GL| zvb5IbMNC}5*}P`k{YSNtvPDrx4Qk|+j ziOt}vRHWAz`QGq7nUII5yVJ{pf1?X(w=X{|rcRGt{p35&;`us}^1+x2=#lHq&Dm4m zEHFQb&wF~jr8jc#4APd&x+t5&+^u}J`WO)QIwzT{IF!>&s}!OdbJow%b@yO<)<<4Q zGo=v~8*hahR;0RIq)U+eskhX^){*-`zDtGQtuNf&JtS!hF3!uA3{AfzzW2bO7Lulv zMIM#KWk4uQ|Kg7$!;-2?x-eR{#q!(ME|wdSUzX@YMW4C{GI~Fx>tc|0SX+~WDu-?v z_cy&;`RGyd`KN3AFN1;dF9p#v@qTsDa=#7Ol{v*~fHv*Dn88Q!*y}m`0v8#0f_PeTJ z?&#uQZa%N*TAMN&k@`h=<05Rm%vVkt0f0+roqFuvnZMD@gQ+POj=rXBM zXI(f;kpI?neysj4qh%E;>|Z@VI+73=H#Ndmy9fF&|a#4-#03aKfI@2JqDR@Kg zd$t><>72+c`X}|WKSM5z0r7cvj29P+W4A9UY6G+HzQ63AE*UJKoN}JMK@#zom{n^k zq2GjXX}nNlu=mJ%*5?9k4Z z9*)MPl0xuR^T==`M~+x2@r5*+$4@(CFSuA*>*+K6OflfRFVvql@BD{`<*=Bc7r%wf zGoazmI>ROcN9vIwA!s}n7O7UN>+#ukO#skydYWx4Hf`A}+pZ61CYl-=Gv&_5T(|9KG{N8W(0dp)5Xh2^4^`Bpvw#CG#D=PuF1;>cg8HDbcQ=S`8sKsCKRzXfy5zdLcp*=a6qw+rXkP7AIw&ZYJ4MnQ)mHm)mb;=}d zijFvfkX9F$&}nW$+qgx<-ZA&hi7ZNv1DM;q9fk71h^g{DCTehwNd~1#03Mzkpoyj~ zhNWSIxGlG+H3WWTz~(3ka0OH4KDD^SjHHHl8fuafi+S(Uj}u1+W@Pqf&!E%mV_GH+ zy2=@P?LDynYQ}s24IPmJEw-K}>a@-KAnGFPRY#V5w0*pS49;OG45JS%saUbvpe^h2 z;II00<3q%8X;hjX;yS@G$D6MS<<#1#W&{%7K2}AbxL~ZYWpI_9Y>st0FJR@0J0(eE zj{Y(s-P6?It^j{wa_8jyA-4YG{Q{Qihm%Fz#oaA2`!eNhRW92{VLW(Trqen?Rm$T% zKEk?exyxn+TG&_9#t(xmxJR$c)DMT6*OJl!PHs$bmjS}%y_H@A-PKZs9HCGG(ROKd zz!)QfiTBa0Quh-Rf!2CGkUya3GOU?6DYd*vcOO>oUN~K7i_v1gJ{Cu)bClH2-W|eQ zmZb6EF-177)o+c+P}hNxTfTshcAtoZ4dp5o zFlqqO%3rr~q|N=wweA=vr1dspP@~8pL@)oF1BCVvIyuDL1*Vp<;Nk`~MQBUEz$UR7 zAQ}gw@n2ge{6Rope@ir4^wLEFbYN)Q_5SOop2t~{|5FD+TxsarPEZ8zosQKn?%raI z{kd8T@*X|XjhGXIqQuS*BnTIgYUu0W(>W#QyF-+@fCPgx_`W)HwL?94pH`Y$tcV6T zSo1DQ{}l_rwgAFiBe1J@qK`)F6di{Gfhr>ce3Pz#S0Le|ms2Qn$%?Qm`*h3XOo~o* znQ=()jnQ3R){|K9j}Q~+Vpr2{VQL3PNTOY74bF4>Z5~~PfydqT&gS9v{D+>T`?|qd zVx=QmfPFE*70<>bX2l?{!&q30hz_7lE{0xG3fYbNZI!FsxCXYTSB%RMtbZ~u!z5-? zAfYD=@$i{xEb~5H#>6JYqp-f42ZsDkZBBohD-zjF`@=U!YCmq7YKCEL`AnfBSqgoH zq$m-f9R01#^L`&nx%)!pmy3EfPb9SM4Qgc$ywFmWa~*{AtdhjM6|F#g3t`#*lPohE zv~9>;sMj9mMIKk0NF}Nz{{%R$<*t6=F6PzDCB%na-ng`i@uWcAWgaoSoe$9oZ^yS+ z%w&rYNTXQfpgIA3M|`CGx8eSU>AoZo^uOs>k`JO$Hxra%c=5kk9mgvu$dS)EK89k; zI3Vm6ZMfy6G!UhRT`-*yh7^b7BeKgz4%d-<4cqi#M`U^aTgGi)sk+P$oK3$IO62zC z*1cbfSrpjGw(#Y+8>F_x#~Cugu*XFWJ!bi}C9EAU?=|RlZJM~Hgxh$6`~F_eV!897 zF3P^k9M-uB`Noz1=61#zBEonV3aVGMfOyq>j@hf=uVOV3lbumV!M%(HGQC6XrF@$QAw6L4{#c>Y z8^SxgXYkCS9enfsUNh`sODdO|ckz3A>v0sKk3>BOnqf2s5CC8k|HaPq^|%~_-zWD#oMz;YWOnr2deCpS{!liosOabr1rTtoZ3?GjOCE~lUoi+ zb6Pyu$Zc=95iSPC(*?-Z4I>J@9NX+e`od~1sDggjEd#xm^wKRmXT;O0HYK>;6S&yok;1l?fr32~Deow+RN@r#$===et(zf-ME z2Mbx3v({r7g1VPOg6}`T3~PIwVgK&;YuZ{aju0*a(Lki34?P^LOWwsUbWh;t^i-yX zS4^O08+$Kw5Jc1Cv<{Gy6bk(x&X=j__hm?o{jdJcZNHvBf0r52U~o-#c`z;cem3Cw zTIO-I;j<-|4zMHSG}BR)dDlQjd0EJ&zGK)6BF*(j=26&UY$W8&!VO$jzl>@y>P1!s zWw2>+hR1=__%9zWAVU>JpfsENz>_oJ^ff9BxZ{A>NW*8QRgrEgoxvUVqrwb(gkP|q=?^sm7Pq7rd6ncZZ{%LdcFv4=a>^-(n4$-_U z=3Bi>dcs^4I2HCuYSfW3{gJ95t6WQYl}#(`JgjN6B<+pz#+Ov40i+2!Z2o7?)(>*B z)Lb+ts;Fm;bc~qyLIH6^ETt)$!pcfmoCZI>4I2aEv{i#NR={~6>;)kiCu*BPE#4#| zf-PH8MNhc&4@x6^IvJ#iA6ScK^0jt2O?G+j|7eI6U2&H~taYoi05{F)@UHM#$NC5B zWa?2Qll8V~?cH#W!G;pW6`@Y~VG|gIiO)FEwve~!I_+Ns0;`)=E4Lg6p?PGWPtN&= zm5|o1iJ&<3iq?v7e=xPptElb8xxW0fPgiXLMpRHA@g{6Bsx2HW?Ie8eco+QcF5?oN zgGfpCgaGt`chLJ}7PoW0Sm!idRT zTlXiws2h=vGCD)5ZmNh8gE&8B@fm3~DdrraJS>hBuNpPG(yX`w$Jtr^4M)Ow)v1Wn zOiJU?EEhYZ&)7Z|I@?%@B}-Y9cJ}md>2P)#gApU_KHL?#D4pVKdgYNPVhAqO4O{k!URUsWgO*OxeM@W9TY@qwx3}!Cfrz0Ad@R zo<(}8!7mO2#@4Z+bigP-8GTV{SdArLbG=xje|r_RE$((Of9`n@Wam~OsnhFpnNttM zZPrvf?yl-mmXLZ7#xDPKKH~_2j>N*A3CH(hK4j!mFPTt;2jb_UjA@s9SC zgEk_H>L*C?*Jb5jm`~|&{*e)Kcr14L5gs7lheg`RlRw?t^>WAE<+CW5#5XSWX6;!A zu;Rr#El@^aByMfY>B;4qM7=kg3&I&LsuIQ81J%U|&eiu( zE_Q?`Ab9v2d$*_;45xWIy!jfsvq`6HpM5JsQfO*gvtRcX`@u+^;6 z{gF$t)kQytxwiHMM78(87nk7dpPhW)U-}$g!0$p%!HLQ1c8LwwR^+yw=B#1T)`1seHi$p|m!MvSsfgGKmZ$pp?Hpwqk&<1E4J;!U;_Sk!s z5;4JV4fEU#;Zd3N6V?RA#VY&#i-N6X@!+KA!Vnl67C@}{dM)5?08awWFsrjiOr?X{ z?33R)uMBwdEhe-Rr91!YUMXkQ2q3Mnk2348e*V|l#s2!Aimdzla}&=$6*-*S{VW+@ z?M2#5)SJ>5B5A!=8+JQ*d;Zamd6p&_is3PLysJ?(aN#&;TF6b?8d{Xa*jJ4g!oY4q zv@K?z>1b69@99L*?aBp;bU1S_`fnfTDeWBg7(`;{pHT}y3E)@aMJb#splxy{RGAm;BHwz7cem@IwAZmpMU zwp7-B2^;Yo6Pf!M%v+1QrrdNSy6P{jJ#e;`D=-d`xu@QIf3$bxp-59z>|!@gH!kha z)UlnWEJ!x=i=7u5>N6%yt)agT#OM`?YAPEPA*V@xYr)y0pT&hTOnUA*Ja>jU#XPnW zMeWpoblAke%dBbI512!GI$8a{6idf!i#6YggO|y=E)s4s91OXR_xijMRKAa$oA(ERAAV)GYO|- zrR>btZ((hmDGuf@931SkFd$b%tj*63wG5O85F+@+F|Efg=xA{c65(S+(p4GG@AVk)q$2M2YgHvJ|aS9wN%Rv~q{)>n(46429^_l^M z#ZAy+ZD>r4?tJY2!pC7(56R}>z)?@4<`)(}KhqB(Rk28ENcKk?3B-TTbb1@i782K=n9=~dEfMm;(vGa?qH3v#aU*v{L5F0sQ zhoTMV4vB*g==M%1yFo(Yz9Bod`E(kim^%(ndFYqM34;t(iY}et5`zE#PZQBr1 z@n)5O8&DdXrQdEe%Y^`Z&+6^FWlG*xcbTL0vnN|pJY^#Z3zAHcv7ez=V6V3I9ghcR zpPR_*$2r^%^B%Y>@u8pVh)BB}zpHLe-W1xqqn$NN1(fLLp*oRYWP`HU6k@o6weA(-bkIiIl8`SysX}kaf1fCmjdH4DUERmaDta5vT$RG5-0n- zInw3cd4Kx;bh*{@zSi}k3Z3EJ?9=o1{f167G$lvs;Eu|5qFg=U5yve5HR!yL9JWoo zVJG>FbAy$Bi^8eFKZA50Z600B7Oq_@i0+Z+w_GT#{%fH|qThazL2`P>47uM*uKNs@4RQ+HUp1!ziaD^<}o3+A?4xwkIonzbYWV7O{#!@5*(Klpf18# zH-Z$~4-Lmd2@W0Jac#MI>9J}TGWw+I4|VkYJh{68gX!r)FH`q6kh)to3YNsF8Ulsd znvJXqUJ5L@QbY@YHCTNw#+vEt=SWQq&p+r>42+DiqB?NR_B*Qb3t-nugu)K^0rY~` z?vumHn%FVG`erJ^rP^KCj-d;Ab=rEfdJG0$aOW)8;>ma1bTCTjWQ-bGJybq3-xt_kfv>vU{D%fR?#uB29^O(_C?%%xk z4}tHH!%$wyw!h|L4!yVU9jr6$&w-n({_cM@X>K>ZOc%|FZn}IQ^7H`r1a?oMNxSUZ8JY! z4G;Dlf@#nk@$|TV4;`vcmK{HL{4rV>39{ZufQrj_mn@V}Sgt;t(qUVN_1H!ocy4I$Sg_@o05*5v)lBA#o>= zpW#&P(2(vE;Ro7*?Rt{kfqIf-)`=7QdO?J+>1xCtbms9u<@c7PRsrbDI!`MV)1sE` z8$d<$iTsr>C!m0wRHlMTjr>_Msn9W!SngxiYYzm&+@xYEf=Yl17~~2HqJ4A|#p1#0LD$voOEvN7s4{OboUK80 zZuDhZe+WTnukm(PF7D~@Wu0G;yV9a*7=&D>*8{rc};YA&e2IBi(QkPYEQ1 zhpuqsK?UtElIWWykde`gOilx|_htrqbt&%Ksy|(u>FDkhtB{382Kuw~!U~o=y4{wp zPp~?SYRwgcRe!6pr63maKNLzw9o`&uq9qjbjYn?+_p~2iHTHrq>1Lm(x_s_47NB_F z=9{eKB0h;FeSeY+snlW6wMIN=W2%&HXngZ7tn=-c*QAJ$?8!EkN88^>cb~gMo)M+d zc;FwG7PMcc<-pYN+p|1?&zWmUdSWn{1XnT?&%Ec`Qjw%9eARzv$>4Oiu_4o z05UHYbhYCy?Uqc7cU%M#HnFyTtqQ;WnWn>lcvb!FTBK8|Vd=Dg69V{ry=w??yCPg{ zBsVvQ*^9N$5kjbo^le9Tpv8I;?Z=G=XW{ySO3JGrUNx%wckJkNleFwV!S=fY3AKHi6Z@eV#^KE`~C_mmqy*pf& z0nA`~Tz;RzucQ4*8$r))3=BT*>_TIL4^!o_iw7jT#-v<^y-k>PDkcSc&WcpiQ+lMQ z6^B(kxH`HRzBGKE&0)kLaY$wz3WVED^*ycUaca<2x>pI7hT!E8ZY)QXpR5 z#<^Z7Fhjj?RLI^Q^3L_t68M+s|KM9x>KqP=xp+kIvyoiO?(AOI)b2977hDthwS1#q zVlERybd32o$r2nh%xOll`xWqm4tGSmymjSCDRS=|Z{m0RYqQC>R*7PHA!M=aaMijH zY_fNSa%WV;Yw&!qclg;u!6JUWAfXf7)mFWkL*;4i5G587o4h^i8|@w>y zYc8fU@$V8L|8|~s4LAb+??~Zz#dIIGmI?P3=dgZ97Slry1vi z8IL1V7PjO>gaWDTYnlRCKZ&|DS!W#qG-KM0_r(04ls@DHTv18MjpvQE1dKqe#qU=;`TI z$#n=&^ifE^u&XvqzWR+Zg;IXAn!yV^M2e%+M8!Kz+Dd|rx824 zzFB>+_Z4iDe7*^JB-JXC=!=x;t8d8Wn5Ue_IX<@2BD?&K5dXtG0yWx`eEVqKNQBAh z^Kd55pGd}oFQ2)^NYKOu(*ZS{D@VlZt9|MQld{}MR<(Xn2_9Yn>G4XN(G_Lk2`Cq{ zSk!gC*rX!8q2UGw%9C+Fd**vz831>koj!m5M@KpGJX2RLEh*bB#>m|@iJ+DnbGHiL-D&+;<0XFiJpFy zQD{~96(*0011g}9i`eE%$*c;7-mIwRNI9lux0XB@^MlGST_c&=>aygyCt-}AYH+E~H!9lyl`<#)4+Zezz;;#rw zL1*y=dEqi2>%rQ4r;27tq?%__Oyn;vmUFTBw{`;`F(G-`oF)o$x@tUR?rw)Oj_o-O zD&u5l4|kQ^sy_*1=)Szpj9@Xv8!1<>F&H6p-yS5?ybqQ2`F!KD*4gneDs@PFrt#{- z%j}=^P>y``=mkn|Z+rTpXY%$5_ce>@S>R24y_yGOdUYW7`h12n_To{LRo=jBy%#{u zlW&qRjFOof_A)Z}-oy6T>3itfZO?Tw-?;H;scwAKfiq=$MfehP&L%V2LlLS#d2#_s_o_A#b^7>O~{kA2hSNcUDZZA3~rI7tP z{`l=)9FKr9sIR?wA#(Ko7%`7qO1uBs3062#pVeRnQ{a35F5R-dFXs&(p$ zV~Pv;xqlAao}xz+m;ynXLoj>QXA-}UEK9t(%=u1g(+q{uVoEAV>`|dz)THHV1s{b% zdS0%)mz1Du8KC9_W*{+2Ki0-AhyeCPj*8}Xn^zU~O=nLg3Am!WNbI@`J9I)9>6*vu zD^5PRokjU+p3S=_EQ#by$l0jVAugdEU10L2bqnp->&2{tw>R0JLj@Hqs+Uy`GssnUUtr-3(U>WOIa5NvV?;Lt-oAVhQI#Eh#0g9nrpL|40UTmgxnMc z;#8mNF3-sX;LbEpy-|l9T5i)y3fm*cUMSk+4-Q8$tvh*ivW+wr^8DWOBZo*{n@>dw zhpO1UWE$UFLvS4zygC*YEH#`9Zgq8AQ&x7a%L6`rUrarCuWHxaCaAMHeRa;=VfZvg zT}r`wZ6q#F9^Ra${XY9euO!VmUi^x4LlEb+YwU{C`Hk6erzB+%v>a5MN-;3eq0Kyw zAEWzKh6s^S4&@U9Aw7~QifX?0MFa-H|NiBPO>PtPEcBHOc#a_z{`7=7pk*C-JqzeUlxeHw_y)=4UWD>8r;@t8BIrQXwz796d} z|JfdrRVes4QG-p!->>AsrbT;;wotD z^WS%Gu2zH043coR^LIuvce+b1r*i6T0-_u=cPf9ReET~s^{&m82K(nFBj5M@0A%#b z82H7W)91-56EFYF*hoMXuY!vo*3Z_iAwU||U!NY#S%iewn5|87$2h-qy+eiVj*V37 zDp&48B}n%|15g!UJ20bs6|tgUQz<2iB|b+lKkhZ8#XR_tu(_>1aN+A1)S64P?kDrV zD_Tf+vi^eNyWWNPe(7r?!*o`yF-MIBpW|T$2fuZ%``EkvOUqNKe4|63ngJEM?#0GM zr`gru`$X|)_eydIIppDhEk`PVNVc*-+{@m$!MEV6>gro?0?5459=D|@0^-Qh?y4Me z1AH2WHSZ7~WuKQ>J_mzCiWK*F&HTj!4tfwALIo#V&3gi#7`6^Rfocc+A8tJ;gxwMH zZIkdW)0V#;vB!osIzCEJOZn?Gb4$#3HO>YnKBbP}K_$su=? z7{iR?nFE*k`rtNuz+(Xw5bv*%?z^~+C+D%2FN7!nak7QLIn|T>E*-uI(dmxwZ|LL1 z)dlhxLu`_=a|{^-Dwy#%HygE5*mh+q@5nGbWMp6T;q%7+`MgQ}o#09g;yN7R!w{m9 z`jXsyBoDFBh|D(r8+$391MS>6C*Bwr$$AT-JcAT%hUcdf7Rn@dBKpNWmyIIO5OGsU zU7?$Gju>^Qj@VWyub;Pajmo7FS^0&cwZEkX-qxnC`(MgOGf(3#X=2rJgb6aohA2k5 zUzsZEnYo<5xvE}rz97&-x=iEOE$vz&e@s=n_ut1Upm~5EijbLIar*Iw;Sm%Du(EAi z%d%6%Cl9Dr8XLZ_AwNoCxmJ0Jbk9X|XcsDD+rvPTeJ&s$kS>hAEraDDlYO9W=6j++ z#9A!)nz4!uN!B;37akI&ZV-cjcrLvmHO^IA^7Fuc)`4rK@%I>^t65c6R*>3Jb*4a> zV2MUojo4c2Jt0ZU1xFKD(Mn9_tn1t9_k}T`H7+YO$F)C9mug8#wjL5#GX+Khr-bL- zdGnI7nJT}>O=#Zbk1h5DFETaFDU^sdeR&On-ceZR%F%RyN-3z9JdSIG`2vR!GoAS@ z=z9wbpnvLv`&M7vpPyMtxxF~*{{-`=qhAwR-($BQfPK#?xOFEaetME$R_u)F?QryL zqEm`7{<;5rmReTlx)R7c9-0`tE8r>B6dCY_A5nT0pS_&i8q9u4p`gJhjvVyqx}J`9 zr*MIMVDXjiBjL3ivUH*K{jEt~+)l~s%xwS1{f7~T;gloA-Fd(7=95sBc5RLnX}Y%y znDfM^EpnrXv8|T`w4up1-84y7V3VTB_4lmvV1PY-A=MTu-EtQ%j`bdilvCdl4{b9V z8QHro`gr@E>Oh$*zf|HQ%yf%yDKYk<(HH#`oKB8BrzFpl;+f^O>usT0ex)_(*dZNv zlUB369k_k;F1TXf5uXj}`ylcH(}FMdg1At;kzyHzNH3pmnHkB=%(_LC#Uo^WkI;kw zgz~^JK41>JT2lAhffU@U?5&7dcx_9Z73zeH6)z^o`B8z423NzS6e|4L#|I?cLPC`{ zLFcO)AyBqe_<9E)ZC9(;-b+6{ql|#LidX+%MSc~AK@hG36yXOQI{BYP|67?cP?3P@ z9j(hq6^lSvr@n3oT|Kkh!&Cgr3@M`f=}~XLsV=LLk5Q(QJas~$%vLB8)Kv3{05t+T zM=o%)7rFtsuD>E!McI?EA4<-XJK&tts0s(sw2oePW9x(T(2XS-s!sG{^3$m_20n7p%If%AQIAFL4JhlKPOjZ|J)k#8Sg{vhZ z&JzgZ1(KCN@R60QCbT?g|B9@%2M}|D_0^_7Dny%y`5euu7hzVyLkpfaia4}ghDldi zJdi(vE^sBGt+Tp*0Q=HvW`FXtOD3;D8Y<843OHq)K0dWnZ{#aV^&Z27ln$Gi>#I)= zx|ykE0;M8xkw#U$7f5BC~IsTpq#lq79g)pU~5_~bgz?;J_xyZHh`Cc#+$5gd>!H{J!3fYi+ z`_^s?5a!8JbGA(SEX~eUg%!E7*0c4Q<-i@2k(BeT`}@C#K9OaX+u%GTL~ z_hhLOBG{oYkdp_0@nZzYZS%H@`=?Y*qnM%T!Cq15M?Nq`)sk-(w@HpoGHMD~4pF5L zaK~Z)_B%qW&@K{0(;pVG>baRHwo+^sjA&v*XC!8O0zBxoP(3XXrSc2nO$}7pka1!O zRf>MFOOT#w=6zVCCECkVCi{$v#4>L5`blRyN%p6h z#S<|&IW=L0dxoMnh^tl|vQaXS8ob9{I>kH*6s%zGb^Wgdf0=%JsOnBd#Ss#4-EhfXbU$zSc ziz2*=fZn5%^n*U@Il+2-`=&}(=#aGcb8~GFfDZg>Zk3*yHf;E?A|xbT+Wv~Z1mjjA zzZQhRX`#|GkhXblB`34@gVEw!Ty^5qTzWej**(7_->{!T+|j$O*vk2l568$~EQZEY zmc3fwxtp7wh2_D`zto7y)_n~=pxi))A^wCUielFgsfG{u74ICjc^a2#o+#IXN^@js zwTi_5w6C8=wRN%Bw1$^47eZXM=#w%k{gP~Flp zoAsj5r|{tW>z+iaxe~*ZMV-=Dm;)(8m;rGUFU3o;;)|UJ+QioOPvwHhy9$;a zRli)`;M@ffC4NzFblz}P<~Z?dHg7fmWCwXP*ELb-6eEHvDQrFzw{s7DLm;B}01{Ab zA}*IpDgcA&KV|l8lhi^D3W? znUG#}k)vqDUYK~FIBN`Me?_|%zd#OI}0GS9BK<*(f`fm9xe!XSvvp6xU=AFFAM|QaZ_HEPe z#*>V^x(%5DzyfrZ+ko+RZhyYtwZCn?v6}8r{Jis3GjALzTLxKol(0ATPmI;>XwmxN z-^$-E?JM7j0TXOk=u-eVr0)Hs!bB=vy{^FcyTtAvi_M|f!wm>pxk7dk6uWXr zWx;O7U_7mSv2Lah_|~OPgky_7)~l^v=)l~i%#k=QG1SUBu*yQ@<1qZ#FmSHMj6VYN zw|2punE{{jb-&ZRqQ~?W%Pz$cMdeIURb4I+VC`}!3OW4S&!V0!|6T7(`|ckc3W}RRRF7Z6%S!8q6;sqBxEoD=}p`1j9N8mpzDd?(+8#z?%Ok#I)pkQ8|=;Hm>g$Q zX|(|n%)#1fT+Igg-SlB~A0#uGkPI<_FD-oymPdj$vmv0XdH{Mm2n<c-l)4iXQ`O1l5a&aUrxlYI@+8LbP2V3%4by+`3m#FKe*#@79m)3uPk}e8QuA9 zvRInsLzg90xl+7pINXOWW(tZ&{ua0M?}&s$v@Xk{7C zHK)TOpa0z@Z}zjbtVK?OZdW$9ISx8L?M#&`alCsWUucL&jh0Nxx73RzHIx99w>%f@4O{G0c2nUx4-iX&<=-LZ^zBwS8kfS4n&2ktX&0Brkw za6?cssMUEv&zL_{X8E^|x{w<%hqdb07BVQ>kQ|6Wo%cT|cU@^ygcL3k4oOp9t@trn z!OGQ|D&GBTd4WQAy!^q`oLsrvb@>zKwhvL&Lsc&J=A3(JrcEP zu`wul#Kf|-hiojbyWVC%_=xXq6Su~b2Ud}KArV%JsOvHUHaT|~lSb#Y zQH^bp^H~j*%raLfJx}9&C`&D1F%Re+*HJAKkNRiEP4G8zAS9Nn3b0o z58wddV3YGs`0R$^iUH>1W$YLe)6Nk(kzk4Cywb-G==aP|W^IT$7e&4KX;Po!n?3s_ zbpZjiK%1gVhhH6ZJ0

FKH`vLyn`*ed%HYG#Cm_L$PUXfj;taZF;3n4% zBUlr=Q2mM2xsk>AR~^ZtdMLbyUZY1jRne(-yzvIFyOqF-qYti7%!{4&&il@qFniQc z`@`HHi=WaxL|23cWt*U1q|xx7$f#e6IRjv}&X20&c;(Xe5$qcY@-p1qFosZub)%k? zKJ)zq#J#99XQuehdFs@p>5^A;&wPw2f|9qgE6secNy%7n7+ZguUIgL4ne0f3QiM;$ zi=UcZhuJRs7S*OIVU2UcljVwi zf;OS_nZ)Sahk}lAyX~BH@Z{*`nD(Bxww9)5Nm2|yZg~X-pW2vpGw8meMj;Y>r zJ92SLdyxg11SB7EQXBRBdCaZo$kywETv%CrN&Go|knTmrYnsW?8loT`==R}j;eu1IKNK$>I;Y0+}7Vivt*(J46?&{T>ijX)i)NqK+WMo0n*p;)ED>NSl%r;P^f-K88At-Jb(6rRV%0&WcF- z>cGq(J~-71{7Jz%M8jEJGw*cX;b5h_O={oUIV=C9CD^s!uyPIAbE~^9#3?pK(GF~P z(;9X#q>S405rG$bKZ62V(fgwEou%+BA)hVvmmRhEqZaSbsF1YGAlZrr!Gl7>yR+DB zo%um}cXEH2Br8a}2e{^2LlyWp9zW~l&~eVS1%byFH8(*_LNT!a8?MHtcCDOm(FfM} zqBN24bz+z-$iI92zg_CTf$TNL!_tAvr)M*o0D~Hlg$9U%FIhnb5%A;{eFo<8)3MN} zl_p?uY#PwKFBt2?dOz#bt=9i}D+4X$o_RWp#BZT6=Ss9;^r1CdR+sQQnGqWNZA!e|RgrP2GMfy(}&otP36=*ptK_Qt#A$y~(J9+%!I$X~i?%x9;?Hf6d^ z129}vV5acx?XOj?+Ae%q4bObUi&?q^P=XfWrCux%i{*LRm$1zbb}*7ws*95Ju)r(x za6ygr$IEFG|H(E#Xm>}OpLk^1KoYD=b#;oLEV={Wi5e`NE!)?Z-i;SmXci#?OsY%6 zjzh16ht-Yh>#rjm*u@he-96Dd?f#ep;+Fa3K*5Skqm*3f7vn zq4}8C-~x<^I=kuC3vyvd)EIz>=%kPWK-DAx0C5uGbrvV4iJ-&A1^u-=_ruK^PmAXc zpIG0uBZU&xUQ31k{Z6*kaK`t|u7=!5CbYl;ovH~AG>L(;jF8Xb*Z?fUei%`Hnzgoj zD4k8Dvd_ozKB-{S`KK`3b^^d7i{9W4Yz0p_)ry2q+VtO62AV^2g(^yvE_R#tL}zz$ zY$V9a6eGruR}~(QUTlWPD2-1?$-2&1`x1=B~AT6bIcS<)(cZiab(%l_O zck|Nq(s?)MeCK@M7{7bR{f99a!sE-c_g;Igx#pZpx$zK}roeoCiOamGYuC~40<)XpkjM|w~+A2E85 zAkY$7u!n6qb&vGY<}rrf9ohSs=IZt1n8UB%@a8SZeW@m7kHQq@TN~fqr<|W6dN}0# zbzwCBY_itVE3}Ho@Z7A8{mtn`K{u6cFiz1j0eb}%CVVbw9L*mW7jNT5s;1rUITm?q z+1-QTyQ{>9cj`hAnJ^nn9-5NsT5pLhuQy3~M7Ol-b~q!ybVw74%Jnw4TFTE%1JfZW zewDM@rCZQtie*oL?iqA)XEfPa#JhaUiMx!v5qt+xEvf1@ZGsAGoBJ=R%3egx8-{6K zEG(rG94Z*t_b<6rR0?ulzynNiFiE-S-xwsi{8$wq8HwWkPt?kM%>Et-R51Yb2va-M znNhA?Lm>VoO_FM)`eS)^BW0p2f9EUXUY;YIaN)zE@pR-9k*i$#(r1Ci3?C=tN|a+J z_|k~zeXSQ3gB6H*uKQmPxr2@cDVN3M{8h4}yutn~_o%-l-{hNE`UfPZq}D$&t9c>0 zNb?{&8hy#(w8I7Zr`0>qNnln2fHA^P{VExN8r;@+g{qcg@5zHtPL3go(vq+J*mb1@ z_ParI@rs593#G3tdr6SexOLtoWBI4OO*#@9|EYnoT|o{Z?tjF>h{k;+H9~Gz!vjG| zS9Q>30GdokcLB%=du`%;_Upd74HvcVsTDrrKAS30#}SNd*$DdnnSnsB%A9+BAaluD zU2ovVKVFchkuSFu2}?cNIS6UOUj3G_dQgpg zCDcAnG+PF+Zo zpZQ)iIB`BlLZ6DA(yscPpU?2dtFy`y>*io&AOOl;sIm4|;Ph)pU+^5PqYq!pm;F{K z!WQm#sYs&2su7hu|9mCfx!Cy|yNXZYR7w7%cvrKIbxMq&)A#DV9~K1BIfa4xyJk5& z#=+Q%IMaH*JTLZ9B<};VpnEPZBzX3DKC8EE5+4@taRjp*mvgbY0<~?zjk)n@x3HnR zso5rNL}2r({28Vn1!e22(AE0N>Yd}=;rG7{V10$k48zma2OKJ;;1Ld<)yHwWGqFlb zc5-N9NKMCoi)JF{-_qLt=}oMJj%V_2o{AKXPg90}t%xS}1duPJGG=#qMk{>!;p%}B7BZNZthe#L|{zcvcSYV@9Z`%VY znZn~FU&JAo2?={dMMeK1eOo`B%_-=D*MHG?D_W_a)6+#T^$QTK1;qAD zv1*m`KWW@XeO@kYx$2f&K6r8BRH9R7&@;S*RxW91ye3uJ4&+2=8$7Ao{um<0Xu?=s zItME-_jecit_3QR!O)K;3j`(}7a2{0=1n9KW2$o}37^1p_@jwL{Vh|;D66^V?Caax zI)~9lH_lq0hvpd;_aXgXAK8<-_QaJ7nWLOp)vDx7YQ6933t4deY-Ufxr(T_Mknq_D zNGHEy!t37JH0p_3!)jDV@hB)qUhC-q6ZE)KrSed=bj04C5af`O3N#`BpXI7?z!h-< z>_o(^goSD+Cdcg@JRfcI!JkTnY6S+X7_nKVLn-gaX7Ckc&Q|%R={*N;yHFr*brmL# zz|E1I9qxU#B`q0AGH)y~At5qv;#tYJdCP^R3a3;$Cfw8-4=8%8h*$ima-XlCKTOYX zrfi*5w(aehaMKE?l76Hz>o*I^f0#md={8&z#)S0s$1@r9N2|*7t+eg}(s73GTLO97 zzP}Kmsf)$Ht*wu$U7V(ay`;2pjOt;DshnodJlS1itVwH~OLVH_aJm2(h4Lox+8e_U zg4#^T0k7xN#BIa#;%1S<{(c%a1jaP#dYitM$L;GURGBmzBCU^j03yD!qqb{yGgYJV zL3mdV_--IZ=rw=rWvVoaIg8tSZA)dK(TuyrtynF>*064y@?u;A;F#j+z6(Mn zw=}_%bEB!q&OO4TOJ~{AY0R4=zTe433i~tfTs()8ggWP-jK9Kr&(?s zyMtNdr@D|plq2pAul9__lf5KBAOb0%wf(u&w-sPN$GfCKQ$Pkj+~?%heeXL8=K)g* zIy#SYt=?wG?;%LxPVWmx%$ThC_CTUy+ucN`RXZ||O!a$6UN zn*uvPUvWjno%e#|A%_aU@&!?s-{qu$jg%AxjYu#Fm4tnJ-@4xA@F_8mrPA42N3`%~ zG9kd0QBt3bR^CeGwn8x*PMb8Ybeyqg(5lD{7dlz^lQ5LJ;*U}9kcZMNHe`s-< zwNL5$eAhOI*fda3QMI2rY*RJbtv$uBKAf!@FfJ;Yu}gEmxWZ+8SNySvtTJ$e`*}L9 zeCn&`o9xY(E;a?CexHwE8|d`Bs=gTJK@XhB1f0=;<_)DMiYyT5-P%Ey>MA8F3c12` zsJ|3~vcV*};C@D-m?79$TQ)T-(F!I)viC5NcaY<=xbBiYGy>=N!Q>uZ(O_!(_VeGA zPrMO7(dvk_kn;UGqu!p8v$SMxyf_f3hJ;m|fauYBgta7IWj>LsPv3lV!r<(+n0o4; z&8})nW!L=4I9uy+>-9u}FUiWGN3_dKcFna_TrINb`X_C_9rQ`^zbn@WCpce@JvU1i zh)2_hb1vb_m73Ds8YZ1T?8OLP*CU8zA*gSn8|X+E%@2omc-`hd{FTOV!td=le2TUR za9ANLg*%Lj;?g-X| z|LI`6{f9Qh_&4UaoSc>4Ytk{oy8OMZIq#l-Ky-YA%L?k23Al);9#ytab?kBzMuKd6 zG=W%n4_6Sn&Tipl#6*yUz}5Dh|eRnp&$lVWp{qN#f&r|kEft^L)WgcgvH(SkYlhc z)i`|qajID;?uDup^oD$wLBFK9rtBATXL7&F~ zNmNL3so8m-sPRfl`*OFmg+zw!{mCr?>g8QQyea+h&=wH|7xavH^UIyz z6T~c+(u|{)hr5wUb(LHh<*g1X0RhR3jG_joT|6%9Q^u@)`}UhzpvMf|ntYY+l^t`g zjfd_98BR|LGSCKH4gXine_YqPOJSv=U!CYI&(~FsRx1!Ex=sK3#Cd5w&Jc}(XM4}0 zQw!6a$=3FL#650Qw(DqxngSq)LER>OU%QSr@b#yidHvc=8=r~X9#PBmWMmsuaL}U@ z{*3L@FFsgoLM^Q^N5E%Eb4hjpE$2z6;^jK>VKd?;O*Q9;t%Hmh$bpatEOowz9Pwmn zFwh0HeW8{{`!>*?fjCie$MMVk&WQnFc&x!Si?X!AbU{oNsmjM8h?zZrJ`;nwd7M0- z-YbCFlV)t(8-rCLn2vC=F@OR3<$;ZZ#{uyHQ$MTn51ccjLr=DP*C8{NhQn!@E8wz9 z(REGWTiu@DApO`e8H)Qtg<%rGj&>zws$?P6cZ4+_InxNpYf=P$*d6A1qUUscp&Tsl zYjlGaFFVAwxm|%4)o9ZVqNnxsPIB7IZ?|f^=9A2|7agz1s-*W8pTsaHZ`)_9QS|xO zUY7X?ORsuDRRIs>@52Xx+#^ztX7)tyVwEh3+jo7T_Rg0$9v*uIj;`2c4nvge^#|e} zi?!j<1@ieAO_5(ciN#ZN1`t0KK7$-B$;b2w=_TB|DbM$Z5*BF-Kdbl;PVN*wUSt$w z(7+ZsL{E||MW##|!ec{jqF5*XOLU+igL9#e2;Smz;1Y<-u{G@FcX+qDZArBsdee4< z3K{8WmqcAjR7jOaW;)8c+Wo2BL7#aV*E#Quv3m_u>o`hq<9f1=oFr_;kAt)4D=^sS zt0&i&(;$>6z$37ph7pF(^lU@(mkjPN8pVuxtc^lv4tKwty*EGTeQ(*m zDrV70zBAUqBhcIU71amiD(#(E-|Av0vjgGp4W3&%?dZM$qbU3d=nA`^>?#VK;XbrU zETy~cO|%roJm8(8R&%T6A5{1NA+9h)x8C_hWG_T-O85^+~|i@i$05a##C6!3NoDl;3g zuDxz4!hBAOpp`k?KO1J?P?95YdEgzSR`c+6tUTQ37FLAQ&RX6PO|4iBODS!n>+b0R z!I8HUSOMGRR`1Q$Rpxoq#&23epg|Z=ymw#4=E0(#_m-5Q`(S^FuCaxT$CTm?bJ zRb~>67PZpftnjR+A%rwly+p4Oxq7AruOXWvwj$@k`r2~k;b94MNAlla9_qr}XG8CtCgc+tKwZy3rKAXT2%h zMNUVQY8)wisCnH$`{GN^jQbb8ZC$|-e2gN~KqNEK{6aBkT3(0_|Lqd7QG?*p$+=8WZp|AWQEPPLtexIJ@a)w{3ODf)n|o`lV;BMT4!W^8xeXTG z*hLx7E$L)#!Hz`ovX& zNGm#G4Y_9OEL?JfpRVX7T9r@F4`w>Lo-jDtDiREpDrJk`#<&B1O`RxZ+frLjFBwMp zScCqO!c+apfVKA5ay>~g#gb4{7H)c@FFV!NbIk{3Ta-jo0vI#O$#f281%^W6I)86uO(m6?@r{cq{sS2 z&GdhUd*}$p=(zW~xxByMnRsB&Ly#vSAxA7w`SBSTgNVI;&C)vppFx`p^Zc6=HL)4T z!LS@_UMa)TbrVCvQ1$mYffZ%(sTckmV(gTR@6I-egXChdBDzi}8Ae@RHMw392$EP3Yx+2*oOK#Q3rpSj;XsG7h*SkGQ-y zYKQ3z>nHvsnEmMa+)k9IFfSz#acA@+^1ao`x#m14!^NklVUp2o$RWKV9KS%F*UhWW zvcs2Y(Vvq1L-LYgL@pdg#J=Q6W?Lp#_^x`I$*KA02xR;gc|db%$uw|p1ij7wTSfsm z%Ckq$TL$c-)*)B{aYJlmMCTHFr~!En36M6mxu*4WwZRJLj4j&D0s1M;{0 zCNR|CXxq$-QXz%Q6|v15&I3Q_clkgZ3&Ld{$4fJDTHL2eZ*oKqKKRr{>H938ZF%~A zwLwlZ`%LRtQWlGxH24Do-SB5&#&(Bpz9jiTSLsC1cX;27PpX?GgFtPjXY^&}81u%; zxzr<=RIkPB=2^>O6W}U`zbEWE^gl){6}xTg{YIc(@}Wy^G?Q;gH26uJr?jZv9Q)@j z?ftm|MlxPoOvkP1-!AAX3dm9*Wk(K5A)*4k2w(UEG$Qy59&0vZF%G(*p=SH|?8Z=5 zoSSZG_UP3>?4>6J*yH@4mKTcwwZfA%FMe@n6!s}PktIqd)=Du16}N2X^ERJC{%PBvokTqQ5Sm-p5CmbyGl^mL#^flh?t#-9N-3oo(VK){g4c6{hXZ>AWxKlxi52*k<3@ zoD=@+krTG@s!^ejM@ENR+)S5NEdHZ6Vy^h9)G(d|up=JteL=sTS1Gw?i%%P2@W0=r z!eEFJ@F+U4migP9Ehcte`H}LNr~g~2BawiQff&_vm(Z0x!EPi40)9)4e!4a~m`;xD zd9)JzC-$_p309}doc8o~9Vhb~={g4gn z(s*vQP>+sA$j`NGQ5i2B=s^hH<_%ibJ&xZIg{|3Yy%7X3RN-hMYYIRrzxTEpYBM`B8MemHGYg+NGSwjPe360N9( zUVaqG%wG^9o8n_ln@k&oeX5&DAjQsgBkI7)^;qL(^I02n|M#ZMY+X2{7v;hUF#nDl zh$=CeT)D?1&V8BY>p6W2Dgh-x?3Xw*>yJaOFdG3?0sA zjf`_)@_uSpw+b9O9@jkw;?&Q44iE$#6}y9Zt&h`LeuX|xJClKZu|8ZgHg>C*!J#;Y zr(4Q}6@V~B&hKCJPrv;D;;;zhv1GLiFtif#z!(ksq__1Q3OaC%uGEmKgD ztQRx|k{MsHKK{WJTS)U)tEjat-szMLUn@LfNs2Ux``;x0Lhk*%I&yR7FBy(k$btSh z`nQpK*Mi%n`C1l3QJH$E`5hGSBq`+ABdLfkJ(1ig3tw129180~=zcJv z{(+Mv>+A}?I|u@=-i$sJI>h-6d*uySh!uhJX$O9J;{S7K%X+~2ZPvz4yr$4W9!Isap|XajX`_B@ey=yCdKKt-VJpf%<9OB zArtXNYw|ez?6f=C4la82>S?Ut+sidv*Scp?KQYoY@5T$3R;s!q)$a;*>Nvu4g?q3) zgzUTz7rnA}Zw|E!V^NUdPUP!6uQ(cK%U}gvk6O(>0#X{nn*L+UU!9?!DRx(%6DMrGPCz9 z{`;TO;5|Nl|4T$T_=C!WhJxRVzbub9C79&>n~iS(*O*?Fhgg?cmCGLafuO z++;gxIE3Rz29!tGp_Et3GfH4|K+^G;{F~}XBjqx>2=Y=!0ozf7t5=RSdQnH&9;jcV`rsd_G>6SMQRefuUgXF7HNxbIg`*FTE9lcb z4hP!%J7yMN&gI{gB6$} zIZrBS7$tG3NI?o1aI$%x29z!&P4z&H4;V&xyYvK$hJmGVbX3!^?sCgFcTUvAgMJ*v-h zgW`(@qCEc$_f?PUQmfJ@2x#g*&R!m^NC06k9U8fCL}+1oO-X^wmz*;5vmIZlJ3vUK zZ(LA*nPfAb&kpRuqX-B0x}TIOHijo`9N(UVw;h4rdeOCj`yheqKDQ%0#m?$P|5=z1 ze3z!hka}rQUwTjT@xfIoD=4RxzzDIB@Nm^+^(;}bEfE74wV&_fkJZU_q|69Fed&j= zCwdAdbdo|jsHosZ38g?Ehh%N7YUC-Wp!+Ej5s!JUTO@ID`}r>g3Uf0NOg&?dx^KZk>MtiOd| zFASWf0 z`C=rM`il3%lJ}U2_)qYxnGjG8-u|NfXSpxmrbVOeH%6AJz8dct@nIcm{Bk^G_+G#W zzqZ-K$mrH#_pV1;5->caXe<|;kDqdkz8K%H>0nK}u})9bgcesqhn&n7;KS!W{=)_E zzMChl#ct^jO-n+n*-?c+ntDMsYr{f~h z$@~29=l*;$Vh6W{*}>(9p|VNgQ0UJQH%8fi<3+%$$E|sO>4w+kj_+V}sBEB9E&u7U zGltT=`BIw_0N{HU-8(0!3bNnl3dZKjK+&p>gNQ*IB%jP-M;yDA#(G&RjiFg-T2*Ly z4~Jj?<_<)C+$tf|>|oG=U<6P5Fo!jr^@T>g)Ne++K0WwZvENjpo2^J*#WqE4B)fb& zETC(!oA9!*7zAv8mG5$HgmkeLABipZ2Jvk;{}{8%C+9$vU`yjSnBJkZ@xLi~6>`<- zL%&e1S7(plePczRtC&d!*nsE&i;8<8fM${q@#w6K7)yT#L$8<-&=Z|m_G(N_%6Y*U zqzV<~`BgmDI>A6`R9N^r_!+o#{Bvbv?aiJ%(hSFoOF0P1q=hNbAk}rbR~}ZFr;n_k zt+-AhFCKGTE~{C2e0C8?Z+HLf7?yq{$;3Cw)*@aJ8@=9YTdcOf64?MQzh0Z>AcD=? zei^yz)3F$%1$Sz#7a0E@aj^S*72w#z@x<2i59O!CS3Spi#Vf(S8;U0W=e};OS@kR; z@p8bOH{bPRHM`)gIwPC5BGh-8_Si6-f&;@E6l6UdG(t9uN&Bm&IL;TX(f!C8lxmiS z(a<2xb1QTKSI#sr&V6&0i}>HcR?Ajw#m9Z38k}BUDPZk9`wZ>nQZ5cKb(#*n{MslS+ z#%Z&7^w3!Iw3S;thi9eY1iC%;qpi$PLOm|SZIf`i*~qMQyAQT%ApYeOmxFn`a>Q?u zhqqRVQIXScJn7=#0r~;Q`9o1-qe#8sm3%tCg1hxch_GhKY%Q3sk($SL7L)*)998w%~t>mP7uytcVw_&ImFuqXluc3+)X2H=UPDni)7{B>C znmjHj95x4ad?S&Q*Yt(1s7H4TE}O&~EnT1TnQizZV!(VG6B&ZnK0roTJ>g@y-2F69kK;>MX@KbOuRcbU3+T{+WyiiG4}k22|*l2bBM>Vh;RC7 zk}}^tGjSl{WuW5Csq(~eAH7ia4I+8IceK16^RHO01eMAIqaTjWerLtnHFyaux+wID zX}$mi3+atVHLtXQ-d!{tyY)e+i~jV+euV=1TKQVf^6Z2Kz!ke7j`ewe#G=ypvXrv% zv)FF*-h817q+MKm;a)iDxeZisZQba^YVjr7Eg2VGN*qhY+2$`|x#1p~7qC_p)Bn6o zOY6LvY*l#yg68Ki(e)S`3eyQjiC46JUlHs}G<>EA8Rh>AZ#@DE@&GAn7A|=dwMGiM za3Bbdb38(9k1_Rch|QCA*u{s>(3r5sBcC0Fy}4{98&=s!wn@|VpC&fO9XAv_f694W z_Cq%Jsw|hzMNu$28-KlN1upE)PM1)q?LmW3$vFc2hs{9i3W3A@;igeqeAj{fi1Y=! z$}#8VzL^?|nA^Q7E3-_>=1(Bu^17dHvfhjITE6%+**CKUnK7}Ma&Fk@@Y>ryLUnRJ z-=nwlb{!^dU?TIHnyYK5e6uv1hVH2aHZ{#~jd0jx3oB@6r}M|t{F^6;r-IWe;c;86 zSvqc>Ls6r@)T{$@kl}v5hp5p;mM~4<#?Et)-r4U z?$du|^*LJP0G8p>S@D}xbP%0FN_||9An+p%0I5}*2+xDxA9;BFddE+toBaxlEk5Lv zCAu8}r!`sPU19ZKc1-M`^yEHhMH)p2@)L#X{=L02ydLMpKlXu3L4fkN9??hZ04o)E z58OF?ruPAo(k@Q#k^@6CuQ3Q0p~W;s4<>x9_W1cLZTlzA#fMs?~aEo9wFO-Uwpk87y_+w+^_p zSda<_lhY|Yb!=~KV2H%MziYpOvIT^;u!S{w^nam7WYwrJmYtY(CzW|Z2+~lb%!}Q; ze!kT?n{3JOgs0{)LGr)Jr73MPxY4E#GO#B0zsl>ceHl^YG_}n*D9|WfJo2uw4%64> zR*LKRbWISPa<>a)L!*@tdfU&TCso){Yk>Lm*r>g3N?>U$(kK;=jus986mnOZ0O&9G zGosNSR@(V0-@}qJ$Vkz9CqjcpCzY2X1LkTDMe9yBre{x(!5y+VkRMWk_>9E&*wXr; zXEG=(p0T3&;?E1F6P|p1^5IMoh>8ms%Wk+?JhOKJ0fSFCZ?7A`I4_q)EMnq;bgcEE z8^Ol=>)oSw!Oxxr6DQ=MKRwB^kf#yt-7{XZQF6c}Z02{mW=qTv30J zW3j&ELix!{84N!3_3>Wp>1F}+3J2cN_al-823sO$x*qUN(UEH@kV8iT>06N-)x-A? zZn=`I%rblpS*^vmy+E~-^}a6JEjxx^&$7**{}C=OJe?ohhc0HNhwdj8{myeyq-fwr`dVi*UoR<`EIspyD!TH^@nl0`laI!(*2Wq3*(_-T zcdx_b&FuOF?ik@-C5F}S6xMDQ%r#T@tUD!`H3E1R;Y@OV;|e(sChzwf zJ-E6>M0WzXJ1--fro=<^Mj`ysB@0m9T%-N+SePchIa-ADUf^ZdP9dMB7 zTkl0Tkp?q6_g!_{z0b-(oi-!y<(|5)LHC-{)^FMSQ?qEl{3gu9ys@VGYi9~h{{@e; zhC+iEMA7eOmRWmr59STUGcsX!hDAcQjgU*#+hwRbZg7_E#&G(a@8{GUW-4mh|5=~_ z{>N158p*v{zt?aA`P-XlA|Vv-yAAvy6eYAKyD`=nP+n%ifU&@OA1^kE8MXOcw&Cus zy;z3Nj-F78kB1?D8;S;Fk`z`1yrWi+CBqYspd!y^YSc!0gxp&}gMxG)Ok0%f&-glj zGDj*C-fGGki&jsLw;G*j{NkP31=LvHK*a1o zkY#i#MFbE!eeOMzPS%X^e%=>a3%Qc37i+R_U7pX4<|`Yux;N1}wkrXKQ=-fT=xzld zFZmp*I9{EYu*L8ZiLzJdiJQT<+}6)*Q?zQY(Frg8vZHp~J#Q|24Ge+|E<>bR zM`(m!q(|%K{1*68bu7mWA>lOTl6PE-i1*?&M*8(>Y|<^#)lzIQ2~Z})Y@C~PfJBgy z6ZRG)_LQJ7$jKcq07jLqh|C;~dnDqe?;S_W0564w!zD9XPWWId9Sc+Q0$<;ome1}; z4=(e`c%jylM}C*|<|E4yxG!F|fa%}A_vw{>nm>OeYJIZ=7CVbQVI(% zHW>2rjUFTMQ)J@`K6Z5kjl>c#VX2ly;J$POi4TT74rY_q&UCdr0bD({j6Fnx|kS*E`Akp&?LR-1d#OCGoJ-NGJ&G~o6LxZ_eT?r zHT}ap``SwumpNWVUKdOGL`5_BUNx0HXOcIai_@Bjsx0~u4e4(9^3284tEk?7_12z- z4W~N2aQrtv=SszrNRjTdEgKxletsQunYS>d(z05RC%*4ZeozmnuP~;F2VdxyD23-GTJ3>ovvs^yfB+Xs;FJ`iucOXhFP2^ zz121sbbB*vB%HWytokbvSq)ZmueQ#;H9$$vX?!qVFqQ>m%_(j!hl}W5?iX;5dj_;C zGgXSh+b=dl6Mq@^eb~C0F)P$z(sW$@&=qDu^|d_POP#SLmr=l{ObD?TtLn@I77>0fJl#PG-gdmB?Vl((v3(7l7);kBKURwueG*$SK zxcm}K>RAl8QdCNc7SW-&4?u;Q*m-I}sarVK z2!UQ(she zWc*5N+NbH=2ROVb(r4DsWUh=WztES8*&nPdvQ~|unncG@8y%i}|MNG(^AGs&j~4qM z92J%d7-)b%1x?G$+$Vu;Qkf|MiGj-bTZsE^ z!p}0F2jTNdaxy{J9{l_p2Xg?q1?qi=#-k6C zMKY%a^6hwyoi;$W%P*9I;Ndi9A^`#k&%&O*&m97Q8|~PvCyV(aZVvs-3uj^Pd~ z$`>l!DhpxweE=gwgg~~!-|u9j|B=?G8&<4Sg)%DJapeG+zBM=Or<4S0_2pDbi3NE~ zXocN3DckGQ7u1_>gHSN5xfRTj?|^PyQ38J=ZW3NwQT35iLJ=|0i2t>lIBC>mv9KcA z{_|Ic1a>89I)FRb@zHrT*tB^zc>Zp9C?kWEK|I>`g`M~HY0R3&***2Cy8<50V|DZ& z;OP=M>j~|A(Z3`FZ7j$)26Bv9S3Jn>6Rg!+Oav)Y96vVNWlV^XnmgTSPofdSup(9H z=~)>~60g3>SJdp<5H?n(;X2R75;pzcV#UN8JGT=g?_S=K>f_lHHi)OOw!BsgJBa@c zF=47pBc17A^^`qjO%W{@aes=*Y8`mGluSbo`&Bb@BL>a>b#>yTwt`(V^I+nXG2!!_ zvvS$skNSo9`*sfep!>t7i<>|9|Fe4^o1t9j|0O!u(t=;?6gUAy&cHVRr$jpji;&FV znxzAH5R-!dCzO~mHYVBqY!4aW(@NY|!wrWOG8Yg7p72^Sa`MC9G`JNmJM)CXkBqf= z#(3++LUqOtP#&pgK6H|(s{w=MhBTU1$rU^Qr)TPJ|LLX{Up8Ul+02*00wSy_iplJ+ zPq2Xp0OXNCloUM7<)iA|;j?f!CLgBC9m1)LQ`);oI}P zu0MK5nEiG?y(CyXoQ%wTgCna*x_{f`(douqWqF0dpxmCsyZBzZOULqL*5c^qkX~JV zZ=r@9&`r&2%>_B%!)yiTE2onVWlEm6gLJE z5y1>U`LC?#4B9__nFM1U?DvYh)md*q09N9MvN6!M)B{%LWuXdMt7g)#%}j4rJ(tRJQ@{cu>A?|1KL1uhBr7}i z-Jc}2hM-T*?carc+{BjHA%-JJRI+KMvh05m=n_M~26ixH43lx9&ts057^Xngl5E@# zP!z8uuFb};pAk@NJv(@-_Lm(V?B?GXofLFZe@2fPoUh?j~&@$azb!QVE+_3p@99<8&&6R=Q&xC&Gq zhGoCfY*-B>vY*E~VXRLnpU@v9^6|e`4ux*$^D^Q@R?;nFc~IJmyuu?B_Uyc&_x>tT zVZ0|1&%5MhoVJAVhq)XvP40ir^#6_L;Ixa5KH7MUK0_x272tm&{be#h5AM)-F(=;i z{{b>+JF&_y0m|zD|LN(hE4 zuA9kbx%~42bh$-<(;XyL+FWRk*tL%MjO??cyeQ4~k?u)vONY!3f%1Y7x793KGcmW^ z+0F4@x=Xetjl;%JwLCc_8&5Uc1bLDw;BG4oCJ2rO^Mhk6Up*R8(g@VTn53Er9 z1>H_s9qKZT+EmgBPTJsYSv5=Yo}E?(2^ zc~==eY3)5Z*xrAeo@LZilYMBG*`6WfG2cy#oYs*AIZq&nZUwYMUJjB1wl&#W*^4N>2Ul*gtYsuud zD%=O7xJ2XoSqu6f+lA9LKIDKLrF#a9@iz=^0}ksr1HaOS6~Q0zYG@SeY^c2DKSzAd z6c_w(f1{{w792%d#~GsOY`xcgMr^xx$vzDThGglkG;b2n64=-ck5}KCAywqx0lwuj z0N4s=-Btqe4fpGv>Lb=q+;zq+{0Y8vM-sIGLaboLNE#ofKK@Ci6C5eAu^T_CF-T`r z%AkS06*6C_eBa74AYg#X5phH_h8D!p%V^Q{UK>i&K5Pke8bgoWG?3SHhH?D&)_>i~ z;+M2BKxu!;<}gu)sDslpacCyFzzua!X7*O1#Ht{PMP4FP5D8OnyTQ<{xdj)lW3)+ zk+dr*=`OaB$qR}F)dEU~?o;_&R+_6pf7mse#yb?QtybUAon+-phqE2-%Nw5uf3wWK zu0}hN0ZzN>s3JAn`=$7nmD!1+Jp?nB-sf$N@Rj^Wl1n(DJp8y)X2_R0nCX%OoCC4D zxKnS$?c*s0Fhyg3AHo$ey*hxMuRX?i&58KP0r2}Z7%J+1&PK0p?t-zUn5%48QqbRQ zfa>SaYj_xz{r{V6EvJK1*u=CZ(g8&nzsU9s>THz- zq!I@EM`R4DO#h0hLTYO$4n4EF|CbHrM%^lNb-fIYg70N!Ej=<51m&|r6F^l5K)vF* zW}Sua;rQ8Lw0of^gOs##{ocMP-fJH)Y_ZI}s%VP<)x`}$`ti=)!c|ybSk+7F3rAS| z0uKLkF#Squu)1F9OXwZxFg`BfaYXbxgV_IQjf7@@m#q|y-uWNM2iyw+w+7Tn&hisL zMsXX(xKIN^Ce6CAGp%+Hb@K`ueyon3;gxO{eX&z{D|EN<-4St8yQ9A^mJymsyD!&-#kjA#rBI>THm&Qqmb2c71Pb;N<5M$3S7RvSFdz{3RvQ?) zwr1%8AJCYO$giWWpMAi4$uZtS^Jz?c_~#tj#&`LDcgbTAKS$~;Gm{56LP}8ppjo&E z+FKW`dhS)y!{*a(H%RDNXpxt`97RvJQF6}_>;NvACDz{!VZd=$JmswsLS zw`6~qZs_FCIa?kMlnUd1B*G*IU*qBo8ivK5hjRBa;ag6^8(r-4WKRSE3PCJZmm^6m zeomQgwW~{Ul+@_3s3541PJp%x6{Tu7T1+w@PjAZECs)P=)z1iFm){#dHmxU!4x)Dn zQrb0te<2PAq|9poN%>o|#QfD;e45nX@OoaB?OcFvP#_v1N7BI|P&;I)zj0D?k7$g- zn%XXdBnbstpU1IA3jaDZ$1IojjY z_iXo!I&Yv0%JF-DxlLabNQcz8&o8&AH5;5GcP=WZpBSyWLWN=j*T8+os#d5erY=Ui z*=smF@AiW+gWCcRxD_TAaxj#MIPWD599A0l!C6d}_yc!f0dIZ4sB(_l4|~%LW<4N# zXK+q!xNzV!Rl$;qQi%L?1R{d1qe*R@;?nqE0e8H^7L&#AO!R-bB7<75+y4U5EgoS74~KfYmmGi6qc-tq^LDQmyxy!~6-|A`S8%(dtIj1VKVN;pNARU$AyUSxEUR|t3DIK+U zG{}h+Ve&KsA0mE|+n1E*61Qa3^j5YRYC|)0F;pm4_5X4D{#l*RiQu~Qcd_i{`1g>u zw!jO9LBnzVibaM_OJUU-^pSdVhREsO1^^SsCsJzC$DH>`d{9nc@+kVHnWL~$AMIS_ zQ&pk*GPq8wQ{SfZbIYd+`%9*mwRyk{uIg-I>2SqnmE z-f3`FI|4cdJ2OQ4b@!j{4bEN|k5K)DG(Z76hKbY@yjhpidF@D2xUCBN!@mpyHW=i@ z1Gjd>z#^6xLTh?6nk`|_eY(wZ^+=5JIImCO9`mtryYac_W7er{)qa&YqVH9O<+<1* zBG3`%|3HSY#Bl4ZC_A$P>3Wj@7dCsB4n99BIx&|L_ZUFl2?VUu3N}Ud#}L6l3G@8H z{{U=)rH;9g0{*pjYe8?W05sDE;B)TM&}ysq9Y8*&w*OPJ4m<4mSgq;zprhh>Vyn3$ z?%zU9Q&mi-+t7l`s$WUXK#n#c*ct6o`DCZP&Nr$Eb$bd>w>~_Boh_OL1cZ_0g^R^l zZ&nU+A$Q#O)gMdx!>5S&QObYu5)fhauiHGvQXa!XV6EzBK!M5Uj@!ws^RYLM#?mIk z;pUly+Y6k311|{4zCz&VryxKu)!m)KOCDHQp`?L6or;taE^bu^UrmpBu`1BI_}nb( zm#Z=kK?~+$cXz`TnMAq=E175y{KvGrd*ZE}U}jUjKQk76w@#t7U=P{tt@G#-b<;-h z>fg*JN*;d1Ls?s^$4)**c)&h&!q0FQ)dsH0~96f&#}>`k{d2^mfx=HZlY_4tZ$ecju&v+ zo?OK0B|eLN9K||-fgwe0JXJxENlptrOtyV! z^LMSqe~vCteQ3=3Jc{$!n{X4hfxsBBQvdw$La~-!G_+ds4~}@k|5PSHkTBP{-TM?R zBTBMaF@yQV%^kJ1Hf|;R{fY$3pPoe3g5(&HHwlA-sm_8V#{2pW#~ACzZ_(|S70)ei zp)&8D2|$5m42ACOuf|fUP^Y1~`o4Cr(%u(O{&yk%&qWE2Y5>Dye`>4s%gjILHpJHo ztXEAWn+(%vbg1{mfi4;HU1`hxXU;su4shTM9`5f_?m|LBeD?*MPbvxQPVG0g;Gly=sBWHR~a3T zmNWDU^vUa;y_QzWL`TLysphpGt#qhu_&j1E9vjcMFLHhYM7s^-kvb>lk}!&iETopx zwV~-x`nSKj9A){jxs+HqI;FJ;_{^EaWRzRl>+@L6BQ4Z7*%ZfUmjIQB#FF=2)?+Jp zYa%Gcp$6o`zH6IPG(*9_PzRvxa|J0ophL!H&?>4*m(@$&b-$T$t|9xHkvWd(v$`zIBt$dKlu-?-IVz9kt=_V*BnAa22n30DWbv|gw{Saiz^XJie#R321(;fM?b zagYJTnaxy(ORRdXUV9L2v6=iDjCvlai9z*=0qLA0eFxkO|5Gc1dNNaVaAyLuFC#EX zZtuzh-a$TT`jNJXq>Tfq{5bHcb3;dun83>YL+04H2-PwV-28X=7TY1dk23A?P=c9^ z(3Y8w(>Q{KbmYOhtT`j~gSX7{D7_XTC&&Wl8Fx1;-{_Ah?%8!}T?w5J7ZC}QD4L|s z5-BuZJ1>^#RaP+QzZk}6ElRt8{gut>VVe`O(wc1qbO@Om2|;cY@`(a?+Sc|EiEV^ zt%wrR-Q5i$-QC^Y4bt5u-3@25_u6aibH49=&wqOPaCzx7=Nx0);~u~9j2Ieh#8)-c zf1)=(Sxec6{XyllOMsX+o~cK1J#l;$LvQ}j&9SAHii>@sx_LiZ&1-k~Y?kl>0&mA^ zhh!Z~3#bypITf^D|8gUgPvq;nb&ar)N0g4sfWVofJZooe?$x&6(!0pfMT6E~-RNUG z-i~R30{ z0hHY1^*R(W;4E_!&-B9LKoOU14Fy2FVyRdUf?il$i})m zW98^PULTwA(T>G;0{X}HmnhYs?C70<#elQP9BQF_I?{o_i zyit(ojf>8#XM{b|Y4UHYO%~yRvbxoBWsPL~7hWT`f57w0COp_CUO`2PPFM#a>dI0 zq#5=quz$Cst2fTv@(y!bT;72shE@w{)WiQx8k>IrwwrmJiRELOs9$UF?)ZlxFk?jU zXR2V^VmRfmG=MH>-Ew1x)m1_s?b34?c@)G3OKs%jX-URyOttAh9LA|&=AUVOzu<(0 z%Osi+^4TE|si&+AUR#*p)>xq6gG^PC1FZZfszD}4P&#h z#&^Y6top^0rGTU}lh2jXHCoLu0-Q(`E#=R-vmAK3&pW_7s`VhEsf- zwcVNcjL`M+p45C}Mw%o`z)%d$|BL7C-gGgmo+~*?mjD5FyWkfGg~aQmpzZNYdDyr& zUsodb*Vjqg-JX^_-5wvp94v}as^Yl7-H0;ZIkv!E6oAFM%#n>Dd|xW~RBP_x^vO)I z1+DSk61A_pXJ7ms&OHD=&NQ3C1MmS#e>78@_o;ZC;U0oi3KKLjzpG~x`vdfwul>EZ z@ou-g}XVc4iUzkGMLieatEgv%quKE-otme-@R0 zhsWnAN-p+XVX~`zp##79_n)jNKW9a>!z(4SfrtAPlrcEDV0sN%P(HcvPsa*#v-dNA zqtfg`J^BLdgD5cJa@i`xU@^|5XZR)7(wx$^)bwXZp1D~Kf2vTcfbODeMVQ5Kn0WF? z()o_D^4r+^ikAkh`#`vThH}{U`E4tqk#Zx^Tz6#-mb@AU3&)7|XB$Saj=)uDo7Qp3 zqoYTj8@g3}rlMV6#OkPA0Ei8Avdg7<$s{VN07|x8Qa;_&_@Fuiz7$97GN;*8H1Egb z=ww5sEC7BZgUT-r?q}0|9p?quMnCw8m6#6jzE8xrOUBBhtiY z3SA$e(SWlTNg(K;VQ1)g(!1%b>!1Fbfe()k7<^`1++OU3#|JE>d>&g?&XOG#`*Zb- z5Levs@eHR|g$ZI$QNJixxApp4xvsk+Y_3_bxoJ9u$>By^fd;mHLin!C+ZdAs}bnp`~n3G3DJPA-N z)+DgzTk+eJ@2J}2BuAW=Y4g+B=xkjUM8}czgAp$cy}uc2^U(Q`Fdoo#6PjqM>5Iq$ z`MMS67X-F0l&t&z@f%%YD28FqVYRZXzyHwP7*R|v0cngbqB-wHO8$qm(FKQM({SSf zTbM7xJ4QGKEmunoxR-f|BAWQVF zNGbTZdiRrci-^+*p~h@kUbP#U@_ocOq=HeeMl>S5n%muhDn6TIW!IS9k_(u>1WEG&Kw?>K+F-o{XvD1t_}5R58fzwskY zm5D9RR|m6dI3J5{*%r$v!~u=Zl1*B#nz{p0rJ17L|+Tg||d=k9-?81hq@~;0XiB;ZC|FPWT$R|LcjqqM=0(mBVJ=V{WI2z^}mk-mpGa2h2Zgul}t|ZTl%ge z+Mpjb_wrM;qUCyexZCTgT(5`S7@D!79w$@0)yB)xv%7QQGN^MPzUXahwJqhkjEL;d zrtbD!L-a;OoME7c96v?loE#6}HC-Sw588g1|4kS5GuP$&W86qPJbv9{`^|v)OP`IJ zqeVY8ob8NgViF07HK%EEbcteS)#_;fOOiVpI8P*8_tUgZ*5A%7I5gp;T{lbDn#jg@ zm@DH(Iaj^TM&U~>7H4lYg;s&L$!?XYTDe}9Huqx_9Wk#XG3@1hs%2K7hp@-&9;yuF)GunS zwwZWH5qzC#x&?zwfYAt6dr4cwTBlycqBmh0om7|Di`VXGt)ocMS`EV+o$&}74cq%C zeMrw)Yn;`#;*J}^oeT0>_;=SYABbNPhF5s#(~Mr7P&oY#3%DUJ;~kvU{ZWDV_uc#- zZt)5iB=D{WrZ24|ZC)akPW(o8O!{V%>vmiy$pv@qPh zAmlLmGMC_Wr#)y(rXEyiDnqq!lb6H7)r}Z*`6Z%;LlR;NLL;bF&eImFdQa|b%%W$e z>?dYbzn|}#ePOE$w?mOpWKlpISdEMHX^w)e!exWsZaiu z$R5syYH5@z3-H`tKxK-bq_1+kouJ*B$jcBlN>G#L*aG{e4tDd9~%<>6?^T0B z3piWsjKc~#vpD2t8m9is&&$MdWFlCHbHQdl<;jX zGAz`Y-ubQMny@U{r%zr6dPqCra9aYIs>}?VfU! z(B}?SX(uH-+LL5pUD4W&7(kj}ZlIt~^!!rHslaH4*TW^Q{)5p6QPxuzVJ@3nV$C|o zmsnAefXcVDmJsje0J!I6Q(24 ztI3jql`%x(-Y#58{$Jzgb7T!Vb3ps&Lx=C&yKcDkHnQZ%Lt&JE4v0xgicT{ntpY?7 zu28mo65Ii=nuK>w@YT_Bj|-W0Z5XAFucv0IE~N9xjUldSZ@XPpvoZ@ZU4^1JvDlsF z$HGjSy`Qg~S}y588Z(q$6dsPD=AGX6J4@yhG3Cics<2t^n`^@hqg<7|a{LT-;g*TR zPi9Ss#?ysmG*?SbUaL)QUgE9`lN6!Gv#a=+n2`L*dq0$P05HOvOyGk_A&WQ~3OKg;o2N7RXlgXE-9p46cThc z$kN?fnrkKhq{@Aot29Lxd|(&%zt@WLE`S)nS1viJS$6ed&5-;=ta&l5gS&1#@Y?t@ zSu7#~f_qUdryQ3}sT5WLC>Gl4Dh$V8GOCwTd7C1sn#ON^x-j~{7@CKCJ8pP)*kGo2 zTlpQ=06^hUcx?~gC1sp)|IYg?$>nkzE&d4P@(xd*7TwY~zU(*A;yh-iMW_kwoT7j@ zE$cu_hCYnnY5r$I+CE>I8r=0CrXAmGep-nlEo3b4?!?!1RMp0VZ(U0+dhauoEEzAB zf8r^2=SovUwzpGX(BVuTExn=se`j0fp+fsD1*>g_DZ2&UT{pM(1l%jAa!)08R1^Z;CPj1 z1&NxklAY*wPOF}36lq4p&?i>VOKc5W{s{R$(%k<~0(_38YtgK&pP2IZ2YYE@)Dq1G zBcds+qJE7-U&T|cJ_d0eUtQrPvBoCI+)2|IL6Sr~D%1$BflW5YOASZ4fxdZiuX)w-Wjl;pica*Mnc%)k@!(3fo+pCp-L6jBJe)n%jvziq|9f}S z5?4zeA=$z#q!)DQXCCkHH9OG{7n-2&E)RXcjMLTLDLfYS&%=S@7^!N{)hG zUsQ{#jhe3Z*xAUHr^R|tj+Jg|pAb=8dV2awX8^YO!Q6*hiw9r)0x6VzEP@w^oMsE| zfV8Y0jfPr8#>)AMhqS4wi7Z!ky*VrLs@Ee}-b{@j!bSM|*FP$ZKiion3IZ*YJFqjx*Dg z!kO6i)ds0+sTxBOH)7f;X#9FIcZYVZa88QbSfhahGw+XhSh`T4-G_JkCDg_0E_^~ePqCmkQ1hkOFp3{mC(D^lx15h#$^4!Iq# zL@AtebgntOGzKTmtF7H9Qk;FUXpR^mu&d&~3pkVJQIJ0UmFTIr>ofArIXpc47YZxu zi8-Oq$$X6&-rD`8=_k=Lee!TmCRms~zdha)L1FkvKJ0e&4Z^nN1d=9tbx)^Pj=I$iI> z#x55y2sPxn=m1~tZD;yUvs8r08suo+IG*By8#>a)h#&f+Wm=RM%)V1(EcoD!)=AH>f zW<3wPumBczmv-25gWfmen1h3-KB-u#^0X_+%}a-+obRAPQARG|tTGubF@%f$D3_aHy+3U;vsBahEv=$9JR(9L__>8A`mZ)O&C&p!l{{;o2dA zf`F<2JzM(ALXCpiOu0eOAuJb<^o>ad%&Z>|1*%UfJ?yjneWtf!p zp0ExO##rCqa4+DU!vsH^%B)%o#eGY*KKM;=b;4)qs1~zkV_<+I_gHthSUAPZtIT4@ zkj7AI(E*3gb!-#F^WJC5x>G#`^EI?YC_bc2EK}LHphR?MjvvH_5q~JSerp##<3oZ8 z*cj5==DnoX%*z+mqwHWSQRd=3{=D$JQg2%IkM(XPr~Di-}D4g!uTBnCe9I}=O zA3-&|q5)Mcpp#z(ZtC>u_S}D|B)TC&`*R$}x`X&G?rMo8VX+Zbyp+RswaXi$Z?*Ku z^Qxl0=ruF+$PN5Z*T@pE+RTlEz0^=}(+p~=HnOg66fo9G;eFWIEm+=hnVz0cjQ)h% zy!R)H=F2HbsLnU1HO|9k{IX;|=mhJdxA`)Hs$XcEHH8AdD@bE))5Bf_&}4>%$hn{G z3^_47#-I&uqcbWZkYc^>+#pb=ixrS|%bALr3x^N6$SWu~Vqa>ql$Uv=KRl8F&gm`=lx9NPsO_wMV?u2**W z*v-aZlAX(6?9bE@0YIGx<{rJ!=DQbT>}G|y{CG#RsPL2&z<6pq3U`*qq-{<~x~MX} zb!>`(X@PGvoeu{rprY`Q=qpQ$cRLU(+zF7AJ*ivJVz$;J^&xdg`}ti1ky zsfFH>V{o_-ij5={7=-P7#bs4V7Uhs20Mm(nSl`_rjN&1a@8u~|AK03JJtc}I+D#MAeKe7rX3FZ)U}9Jg z7%y!1CS!#o3*>*XXd?tqV!>9I&d`6(!o*UDDqR$rEbdjHP{qnrrgD^2j;GNSe_!nX zxY+E%yV!189?-e$bb092Dir)${w}n$1%gcpL0;cP@^?uKqc7l``L`13lSbtYU+hh_ z&4XKZXBPP~?_>1tSO&H1^{w&bX^ujYC$NNQ6gg4C!O{ZQ<50D<$zr+#+aw>@8t8TT zk|!6b;pOh=9Eb5rQB9#FGj)Ujw`wXGZD=-VD|OUg5ViL7Oyg8`mBjQ3jh^M|V_-F^kZytk78)~A|&+of?8sf~YbKVpy?lzHQ?X3OZZ+@!~ zjQ&{Jve#I~h5F!M5g5}#;6imT_H=G$CWxv8XDfFGxk5>T1mK4dx zXj-_WKK8h29e+~*4J!&e%LhSx|HJ|j0}l`Vf@s1Aq%^4^hT^l|a1I4Ugd25|8^aSw z*cY3j(McEl0BTXc+4tI95r^6h0)J8f^Cdd5dxm6az$5JSov67v%OeT~x*3yJBMht; zlBb&xgaD1Jcf=7ktB!VCD%M;w^C&6!&qp^L(ZVp;Nwbt4r0k91#RK!8sGg+^0aqCm z8L5Eky|Y4w;q%}sQtGxbswXAp2Lo7N9-!vbQ@5;L*+kG_>i8ce<-e{S95oQ2ywJ%K zM*jXJmn;a-Xc+^io_ND4et;AH<=4v+bYg@KU)0#Q_P^ei&0+}+_!0|PsvtyZCDCBn z^q?Jhq^t7AZy@ZeENP2bVy#{tj1ygTckccu*1S0d10MsjBKq`Qa`g)>j~cL|ekfy$ za4%+XnhB&j<<`EbD=Li5rI;+f6EmGo$X0kFn5#CX_D%3j!a6WaukYyS7#nWGARx%A z(XcPFGsEXFszk`wOU_NgU+7T=E!0WJ1orPx$heQ`c|*er{62TF@@gAHNxYolttk}o z^DpgD86%>guo{gm%=LOr%g>_vMHe6m~=pNm?o>K8*+&Pn>)m(qawVo&4Gaws=y4T=-lrwwersRmS^UROM2OiEyYv)vc9T~gv?x0*HlW{=Z z-g5{gIp3e*W<~E~xg~Koqpn!T_+BM_QDn9GKRnNb% zk$>Yf3}gMVXEsgYzOs0*qqgK<_OR650)+_iQ`Bs>VUaOlUY8edc@ z{aKC<)){|{wsl~iMPn-Mn=RKPgQCl~;nx$1qwDZNEYZ&DM-gWzs#mW>4T2SQ$SV^u zwva6ZswIvjy#A4#@D(4i9IkRvwn^yw_EG)8ue3;lk}xI-ejRA2EoC zkO6U|6F#@>n-dKVB2#d3wu{&@*Z)hP|3_2$J8rq(VS+;BE`T!r8HomG(S zXkrk#-5occqhzv#dZowzTZ?)qB!z{}5zGyW!(3{QTzNyoqp3}Z%A*S0Y$z{Y1Szp` ztC1NZ6@99*?p60H@|D&UFuo*Cl{&MmcfKkAHo!OLC^WWkwo%r!E%$Z4SnCs)+hL;7 znbXw)-d%t6cR!iPVmfoD!Au6?QtlMQx5ItBpT_pfFP53BD#fG7OG&L;r54H${0g zOOz|9MN|8UVTiSi9x7=zJY7}v7Ngq&6GoXquVpTxN?}{@!kcR9%B72gz|UThhwB-~ z-jP|0E38kIc->`4>=p$6TS2m$@;@8um>GZ>5n&lwIG`F;b;`{);FCg7ShALeNr^ng@7h^fNtjnH6hG&z zl9q>DJ5Cd3K)QCZxx+gEjn-@Y= z8AmeesQ=Z`U+iML3!cH@Y~$yYKvdhra&4{s3B{e1+mT=TqvnjQrZe#+FQn-y4zs?n zLTIkM**%4?nj+D8cu@amHj0SWrJf$-gHzESXzlb+9A`2qgGRol(#BX_45B-!nXOpA z&L@F9?ar#w4Zgqc!~c-^g;#)APtY)g`s$zd=cqggK6B~@J&6yummYwU*f7XG7j)7F zPbva>B11596W&y6rb~sia1OP_b6@j*XX&kXmrgV?u?7IOKCe+p)Tr_U=NO`2g4fsk>rsMsf7(XZt78zuPm=$!!5b)$<5T;6fC~oK>6Hq zkEQK=l4X71&o7iMbw%~(l|{*vn9>k!eO)~Zw~NlV`=`4rotZK#_;9HQpl*s}%C7B! z3nc=G{Ha=Zm!p7&+QnDSX#cUI1^}lqnjMf2Df_eXh`xyCguY~9>3bD)f40T!4%Cb9 z`8}?$^O)zWO})}Y;euMJHL$q}uQ$dOJUUk_DX1|@8f8Dn3GmNmqYnOB_1; zVtar`amS>Ti1+NFxmw8obk;*UpgYq;T0pW;Z`$B*QnE08RLIo=0GuqVduFL%a7#Z1 zTiB4@TZY%=503NwiKO}Uv0j4acflnY<|dd9->}lXR3UXt?pFKt5RX#Y;3*Z;-J5ME z#2&iDa%#}yG90ncD*#oRIip8z92=PK zbx_xe)dqXe;h6CR*|zZe6~R!)BxOVQ5ZAHVj3tk5L6`C)#dq|KjPdFRDL<;J`)}R^ zyEnC2E>857GMRUACqXE*7ZR@4qVBCe_D0;s8v+KCw#0DK1DH=?kKq*keYB>t8Ec;{ z^*is?hh{(LLi>@9-PWuAZVu;!Z>+-mZc0WQ++Eddkgq&s@wO8Lgp?Y7l4cVV6ODhD zx9PudTp9PCX!-sDE2TIS9_74Xe+Q-N;`&(LaSZx}BLcZwH}+&cBYE~K&p%k`Or73{3MBQ_G3VdSn* zWu*9Rc%hIj^i#Erd%8@^^%`b`9XmWEiK3x7$rL2fk_VSj6iV>AlKG;52Iom`|j= zsp2XlYFNqdp-{(Q4;2J=kW>muP%15uvCC)_(oeXpw!izY|9BY@`2_w!-k(vCtk=8} z3i_Ckde_1{@4;l~E>@^=kp5vjt%Ls^g!LggFVr^=G(ylBdJXe*8QHj{SQ;bjwd4*q zQfAV>Z*g_GHa)>YCRy)xfwfYhI0pt})!-Yj0YMxB&mg^-^ZOi*W7@BzM6%w8F}guj zo-(o=!l#D@LNsE3ci`vq7h-#Sd<3cp!MBA>#PiOx0@>N}JkxJ(Z|>D#8UPx+L~m)K z4%dZcTY8u}TRwp)Bm>tcrU`O&orzjkH8E^DyuF3N(xE|<{8@S$EV{IL#K8#YXIL4|%_%3p@9VS|kqDBTnK-;*IKT;v94KfJ;raDwBpP4yqc*(}qn zJFV?&5ahek46F3Vh-Vy&P(nL&ClZ?~R)>DlKfeUNJ7mZo9-gp1_|-r-7%kPKq|vM` z4Gk$izEGToF*!y|L4~){IVA6Tty5OQ9pvBPlB}G6L}Gw6SAtg zTvE}E#?wrNR`7}9tBzDi?mJCF3yC^)?+v~!r}3+_r`$%-YWT&-aYmXGJ&l#MMBFxW z^AR5|w!mK`=f=`~n=(oG?mC*N*tqh6z5Re|)&14i^p0rr~=6gC3m@j5V*OK?~xu zkNqNF)7_F2i~TW%1=?bx1@z%k1ND}$q9O**B#nXP6xpY2tMcr(*%>;a?0Wgh#GZv} zWyH4YbqvFwqwGt_ik&9SU%K7y=A;3~h(2>N(sP$Q>@1Fe8_Lst`Otm3Qi z#2`UDuVQqkHqEVN3Ki^R#@)uV#N&_Iu?nvJ+XjWJ2$067tHl$4uS2Ij{7&cr*oy6{ z_9ISPg9HdDDBg!)CE!sLCw^~Q+|r%NC(VENj(ySCaPkm&65?BQ)1=SZ?BZu#wEHP> zKp|o}&qqtlL^IC0?25eDAQ%e(hL@W#P}xUWz1%C_i0gVoZchOtWgJEm?f?KO0)#Db z<^vx8K}3dfCDOI_x9tzB9X9GfLIql061QaF+3v@_Zd7)}Gc7B(gWR1+SgLLT;~7VO z7)x)ilqnqYQEE)yfHQ-!miLKF;)!muYtB4OlBt~h3d+UO`mijFBK_On`fa37P~JIT zM}_v)0cxzzXGrBwdFxR>`5(fwn$4yh^QBTyqHn)F8i?hyL5PQ35|i!3F>^DQWK0Jn zN_skV{&l0sJ|i3(76h;MSN-FR`A>YS#0I65?Z?EZzbmO;b&#{CSaqKPuXJsMD?M<5 z6BlbXdn)0RBCPTSC`pfEVuYgy!r*zVbp?uM^~agI;tvz< zVf??3@cJ~mZ8K267C8{i)!XiRqxzu`GRS_Gb+X=AAtXdH>Pv6Jwlx|jPoL6{9p-z# zx64b3yy%uJ@Ur>gk|>DK4}}2f2uxoM|0EtrUV1ZUF8;JfDiaHPR5^z_U_pF&(sK+_ zErxf!YhWY0tZ)5S$2cQQ8EVr%90z`ErfTEW`E!Jv zAFK4*NEZnTWHEH0#B!bnVvhnOnRtlhv&rG3XWhn@Ca&j_S zE^bBUr`>!`xy?fN?93wuEi-VJ|`8!SLIBaniutTHu@H)^dZEj$^$J20{wG z^S~Ilx$}$IlPL@2Los*a-3U>BgVVD!ua?J0Nv@fV=sFl;tnGCd!o`x!^9{_D@NZ8< zC&cpPIe}-&CVczH>@He+_oF;?^|TG22$TNa+GiJ^@cExcWz>NR2MOmB)ErqM0qq~VpqJRUp{Mj{#BVS6 zM|p6$*;1a5inNzg*I!4a^#bO)B4#hnGUPu8y#(nnPPcj^VkRK>TF9p%gF1_7?~MX{y0+=F$^W6eYcohZ9zN;!{hb_RCJ zdvB&+@Xjrt-&gKiS_<3YbnkH!F$0iq5|1rLDEr`eU<^HLKCwg;+piTKn+4*RLfHP+ zCz@14PW3kLwiZfsbY_T2ezcj*l&&7%wtA$?<_j(9GB6rubE}Ly$QfGNaz|*H^K4Gg z?FXE>(5bkKv@rDNEpEF$w>w_Lq;C&G-UjD!_;{Pag)P)oeI3EPDdhfIK2?`49@PG` zXg4En8v$bhF#%>{gk3U>#KSPzRfFTmH$+bn&@-iVR#yyp>XOEDVkvq%Mb;j$HvEQB51vi+Y8*6 zj+e7Pju*u&-)C$S;jCNr{UPb}G4s6JSy5D=pneD5MA5-khl+QJCHEAnxDJl#cREeAL zNV({@M4oBhyEC5Xqmz&r4+Vm^TGSqcfFaLL&(UyRsxrWxr)Gdd;cW4hzQ4eM_2>H1 zK!N+u5%WK|SvA5zvr~<93C<`|#s+`l4R+OppbWYQ#|euB0K9t?NIz^}o){N+>u)N1++RF8`b&e+Y4o%pG5&z8nn>8g@e_uQw== z7cX!0r`6^J-NjUg6hTH~#%!Cv#% zV2jgGwLmJzi$5QNBwjl|aGdXqv3(}o)`)Y5e*Rk_*qPdaV!nzX_St(OcXyn9{$)8W zF&~R(aYN=`^5oFskdJ3A05*#hOcim&t_E}axSyxFfW-Xx4?S~%tJ&ymKrlx}Tj*n;(!Fhc zTtjZs9ZBZL^&KV7tm~zq;Dk}H(~Hl%US`M=?~!2cc}<>Az5BrG_1nRHLsm$Q+(d?XDh9UXH|tp@ z4*aLHCF6bd?x_YgkA0@$>9=pK(dc~^PaO)+F-&EylU#>>Ph}TrZe8vaDSZz!so^)I zk-%%>9*|}J%-vGZv!ujZOZ>lfre#4K0@c;$TFavUJRrY>E`dOPzb`0Z@|;?8$F^ol zW9$8irrqV)IXFOVVbq4Q`7_aWH5=voXD8up_Y#0o#Iyg%j5m186Z;($-`In7Ibo}g z-D}Prs02hHg#=%9IuNXKzbkXZq6oK@NG9{epAoTTSPKFO6aTj`&sks?m^qO6_6-)O zYslx$j}8cEXg^QSOw+V}aMOFSuYelI7Yr5U+J+D-MTpX=W`YHIqCiGaDWG7S4~WWP zhl{lK;n4K^43o)?jfV#hmO@t$ez{&r>j(z}i#_ox?`4JPA)~ljdaM@7DJVp$RIaxe z6&vj>ia0C<@G$(B%Lxu$uG5O+`7F16!;gpR`|(*TB%MXl=l#Zpkid4%9*%%{=~1Sj zBkAxI?=^?pdF&U*=aFKERL-#yiY_26t2@>>AK3z(KvaD)PcwRJK{Yw@6JC< zGtd%4tPDWI-o9_TR<3;gL@8%1ZEqG&w}YlmyA*4gV8UJ{+5cE6HId!FVUpcUbmFa- z5a&(_V_D3lL;G=>!rU&ac2OI!b>A}x^fU(uM?Q{4i zCj6b5|6{5*R|UzO-E_pZE8E{bT!!Io?ti@ly*yCV^2C1I^>%*1+N0-PQo`jbL!wb>6V z8TQi7qCxW|cBy>oe8ouXYKPwkwtF!B28dxvCk7NC5PtFl(&Lw?4{OeZxZHDm4lt)D zCmuUH`1aSwY1jOmj{d30_%7eyVIDU5<4#3lC zc1)90VK!s#-CRcnQVUJ5BO?z;ZE7)k$LNKYOk>0ngiJ^p*C31+cNl54v+R5_{R z5JtbLhVeX|u@0hUmZ|o0stw4?_s!i8)4$|EzE@Y0Mi%A_`i~cY=SZ?V11e5B9@}4jn#kNy@fuYNuEU4YCqYOR|0HlthM%(0d6|3{NI0gAl92>Rd0 zp;ZeADa%@t$#+9Azl3bbpSQfuG`kQTKAigkaZXf93c+-@(dE}G(1%c5qxkTBeez%c>i_s&@lbgOCcz7?lbF5Ool_+*YnhX(%chZ?b!B1;bI1Ar^7^__Vo0K72SZ z1I(+zBu+sN*#fpCs)u=On?1E65#S#mhl?FjgR-OFEa?p)QiHT*V^$=B{IWz6}TLvTcqsx$6LK?NwU6m#8uH zs>c-#`Dy~2d1ATbB*SozMK8GH7@Om6uUUBmPNebOc7GcH=OM#JUjjV9V_z6P8*xLg^VRLd-#A8dAgS`R%8GVk@ z#s_^z1A;@wrPRaI#sy3Fp^@gF~%*n+b z&ojP`drq4{jJQJ#-Gs!SoQ(+h`1DFJMtEhz?j&Xjd%=D8Gx*S=#5-u)5ZwAKjD>K* z` z(x`c!AZ;j=EA~fJd!-v6KEn6FPR#Ep#xbws_JsN&Uu6ixzj{G|>h)_8u%a1eXL#8+OY`HIG@Km5l5_98Mxjc^an=m*9}q&VUw9i=gQu87#jAOt~r@h37t>x z7vGf$IE}MjI&@9tVD_r7IrpTe(l6R_z2Q8sF)9&fTyu7K#fEpAa3$1Y6Uwsk=I>Dd zKXobipCwnPP~=blsX^QkzwkkR0@%PmljPqn6N5QDKm9eeRW=#9##E-e{P-pOs$K(o zI}G%@b`~3I9s;@Gv3tdyElpxG3pn8TsY{T-!KOt#-3eRWwHZ%)80w~O-TMysYb11L zQjt4`1_r+8aO(|EI|g{jcRe#sQLDaO87Mz<+W#uvR1$Wlig=yx7VVfYwgr6)REwV- zpeple?o|y}`yY<-`xI{;`Q$KU$jRk|_1Tc#%ja{xNIVasl=09MFWGi#Z+5v;*=-as zCqp56%SRo-X0Kx+-TOkz>Wj!1>^F?@oE^Fo(<()?_|=GDP@4Y(Dl65FT22s(gHYgbASTl|^GgEm8P#MS`2=d^swN(*0FC+W@RWM7-kuPoh( z+7SFld!X;UIh{_WNahRJJ?YGx1~Zi8e9p)?NJyS<3BB;}W)0kklBKRc zm&-p5QX&7DLxe$oc_l>NglF&Ir0)WMvSyDVpU8!X$E@idN;7B%kfk_GDiEi)o4#@h z)oY$*q&wNvT(t-jh3YT*W9d<+%@(?S)|nj7{r$YJ#g2t7T*1(7np&AYhCiu(Z-bJx zHA?k5G??rbDItAqCujw<&f_gXEa$OVAre8sUY%43;A!ee;TtRi6`Jt-| z!9#frt5MpE=$@04^PRjM?HBGVV0F1`)ZZHi$)t|PNutAbhR)-U0`aQ3LD=id&buu( z%@!Z$(0`tn|N99P8YX*|7g<9WkpGTaLW1Cd*)!~Z_3s3StN>Ap!dIa?*cYH-Ig(g~ zYt1-Kl(`OLhCU++#Y|RP-pL99jnX;!c{HgO+5^9lN%E0J-AdJKx)65c{)S_lQ(pq< zb~fkAI6ph;k=pqTcVg?Bpv)L;&g3wnzcpVO9&v~E^6Y%~_@HJv7uVq~$qp>FI>!u$ zm7_%}sMVRuf8?^Nyo&uVC^U?L)E< zV1(1w*4A$e&lnnKhQB2B25u)u`OjW?;9NFtbXYe zygzEi9=K}0~GhjY3T6H03r z#=yct0T^JEQk7Y)u4kh&i9R&};z*FQ@ zh!&8q)RsD|uQlT_X*MD}Iz0Hyw7iSyzZTCTt#-o!roUHW_dY>5o|XnOLAPLnj*Od| zJI04EYk&FMW_VxEqB-*OqXniNoG2XCCq~wsYq+@UqW18QeFJeu47#xAw>|`hLH-QiF;P4%w8Q#qf3MkCfN?kdKZXB87s9g^jd5F%F{286P`P& zHn|eRwcz0qj;Z497}Oj{en~-7z?~7v?Q(Wd0~VxP5th!9=DDes_}VSINLDc!dCjF~ zcE@@@e5x_=*_$p&`#KDkW~MlwO4F#9XR4$~XBM?77N~F7+I+~OuV9<{PD{T)@CyMd zk=un38p`k4`Rh%?i^So!gs;dc7+5|KLk|xOiITpiiVb+W`oAXlXQO67Kk_!`BlZO=%TV9*)^mU?(*@D+<4fA!7vWq_ z7ak>|H9r=sWGk&57!)9RjXkEAj2lZ8qi+F@Gk;EQ51MJCgQ&@qc&1RExSS zkR~2Tc~Iy8tU4w}-Y#agT&O+trMX?Nu2yv!mku7&XRr_$aFB0RuJ47RysKP>Af&1&?yMhq_Y~)WXipFeC!@5MyIX;nC434yU)mMH-W2 zW=njft`8FDLY7ca_YY}e0c&8>gNua{`f}w9Fy!eGB=?<0D?1wF3?2?nNJJzZV;dV= zJ$$6y`E!bbNz*Hjead0Z7MY1Z9=&6h-x-LT1wX7*{wUHoFglvW-ctT-$$d7pe<4IK zBRPWMbTFUKnPF;Gsh}~yJ#J?aF)^ozHnfe>QuuW{_~20BQo?XM_(7fDtiqAj!dJ_d z%Pg^Md9i2da-l-}XP?1^!@$8m<>tTdML71e+&~>napm#1xNYtY4*AQpZiByG_yNRU zw!4xN67BUi#^gM7gc{&gFzEmJBH@gmf79Pd{Rb^kD+~;X3TI*<2WfqY(0PAJBqsl; zZKW#u_<+vG;^;gq`+KOLp1B1EU}IDc*8cLP)|TGw!nRs=^}DZKdWSNxOPuQvJaDnH zV%jL(={+^OG<)h57{^@@1XRoH0H-5lhy%EZ6uUz-@A(gXmHX# zZdT}TzROk0_*(z@d7IB&u31n>&x z2fBdMjI<1dFr%qZjpz)r+K1aKcsdy#Hr4EL{9{{yY&QqHN^vrDq+V3R!)=M`IXT#; zFJS{bSr5Bj13i&ldGT}4rSiM-Dj0>eH9nQ(Umn2kyKB0F69v;EvfGg{0Y*HNQpd|J zIy#f}--$zs`F(lBbpY>wAk{ioSfgJqq5ZgV34MDc#S5MbURnqL7c(i`OD1wp>>A@` zlB$4L`yUF9YDabTt!;_2DvgGQwyVqEsovVBa)b|8(JH$_-D)Wqg`hD5pdp137xZM) z7aBU{%LM!}e5E?j*P8$K#{dQkS-?!`U}^% zvdlzE9NRc=G5Kq%og9eC0O^34kFJTLU8Xf(2~$hK=*)VUpy%Dy(R|gghVNg~^nc79 zC~;(4?;;w!md6d~{>s>caks}ksRFEaWq6ra_1NArLF$w6w+G!?SddG1uh)TO(-fyo zM-ub@Ve2fTvi#O=4+u&~cXx@DpmcY)ba#Vvhja@{H_{;8-6*Yehw#wdc@}%`_rKpW z&iSSy3>ss@g^EbT?b(+Yb#7@AxgTXAIGkAKv*&TK@mF-%d+P&}^L|+D(THl0Y zFTD_Zq6Yx>t~5DPtUIsuk!!u#mMf_1Rk*2zg;&6Y7MgpDXE9$(KMC5zVo3O5%oVZ% zV1(eZp}1iN6KG=__vRXopa7K&4!3h!u}xf!6VDM3EG;9iV(2wuIiBMJ_xJbXA`>pd znJ|o6?16!BNY5n^(Hw2)?wDJ#F!##i^|lOE{*QIL`i&6}R^!?Hs^!WEaF4;Aq+$=X zP`~^B^iayB+wafyPP0MFz%XG#c=FWq)PxuL-t&|9)OYq*-o<_8M~RPTPW_K%^-FVAPW7!{=D3V`9GxIlNfp>J zdTiCH#IbEE=Iu9?40a3qUq*JnD75jpUq;l7e`dVS zS=d~&QGW{trF-vE_0a4&r>G^N`pkd7Ar>P*)^F&}aj68dIJf`om74Pjn&k(dtRu8K z5l?;W@LP05Ex7CvmzPD*R9^OU^nrGF8X7}3jTVF19%rJzq6Z+~Ph3#!R+^)QH|WS^ zy@R;(DZ0AwENWQKcc+F5vOkKV$RqtKCoF6u8>}8ru(@478w*C`ASZ|3oh*-B--{Kc zI%F>oEcOWIW`FZWS~OfQYLpQCBp4g6EF87I5D9J1DsavA;=&q63Yp}%waizinq028 zCDiFDI(RJdD*@GY?Doq#4P?AP9RLv;;Bn5xo-YDL8X|ksHU1^$zJTO+9v zT)By6Ygg|fj?3wS&<9n<13@g@fc~WW5yK?m(583riy(SqIYM+OD&b3z?fWr53gV)I zz@4rf`^DGh*(d0?3_9Pb1AsLsZ+}Z=gp7!GwYMNESu*bFPwW$BpuSf)5ytt>0`9U0 zL^tPQXje4QIAXi)CES=NX7oaOlM^}Yi}QJ2{K|FUj_NARic+%8bxwK5Il!TnS?c*( zIX%jJEIr_80)GhD^MhXg{+R1=J*kpm2#T~R6syb%af>k%r(e9Tw!!$zu#o$n`mIf# zhKQvuGBR>7I1u4=^9#m83O;PN(uQJ6#1aLBenBN~lw@J=Ejj|{KJJKR{Tw3v`g`|d z177{uJ@QN@`!>%DgU7(&ZeW`PhThEM8U51L8U+FD8M05mSdODPl=~pRPCxk?aoyZW z0kKMXij~oy;^Ab2cGj)PNbAY($%j4)k6)**W2xCC6o*l}^JHUS4YplAlh+b4HN!Vg z{CRj)$ZBUXnQW@6sv0HN)}7CA*zFdf?7zQRjb7&h#e}I2 z@Pd(!nyrm5VPQK#L7?c3c=T?bK?lqJY!#B&y}4rN5%q4Feg}zO^8K+e5eg~^Z_r1^ zG$GJnL*6NXx?JQAG>CY#tAx)1`}Ftk@6WeTOkTjuNu*ck9M)wz&4H%9gHQRJ?+WTV z<_x+uk_{A;&Bx3ruXGO>^r0q$IB12NV2Y)38Uv3Q7qL46B&0*E9*RLU)=XIa*|w3J(hU} zG9hhF_tn+4Y)&hK8>4$|PBk$D%Ur&is;PZ9ny{H^rvGd}tDlGF8gKYnsQPgphVYTzkcvt(Q30XYP=_d5gfL6_z{Nce| z&WFgs4GmKTUZoNt+00eLg&2IddQVHNWlQl2PsYKAy6{Ck4s@N9s@=xV54HyV zQ7Kw!wW+a0PO@nqunQE@3V3dL5Y8YYSrv!iFw4au=N;(-?aVkhOd4eD(Z56k59w73 zg>;?CjC4O{)$5f%LP)VPVpUv%gaWURXygNdg_M(QJjqiy@P0@07E@WLF49!H-Ivc=f^d``*u7eLuFUx}^-}_G==XRreN( zw&QN3<4yIAw*T1FZ9ZLb<)jol$tFm-KLz2U^O*CM4A z`J_w{1dAJDB=)&)L>Oav-!q<*bdOq})oT47T!_04m+rF3JmHYKZzHl_CEf?Xjc<$z zVXbDN!T2-}h6KSHXTqQnVTZJihr=DY0o|=nt`!Jf((w+7V?Iekj^0wuv6IxPAJ_ul8M5f++>wO<_8y9Hg zH>VO&Wsa^r23z**Bw;WM1yy{8h!dB1Tq|O@On{g|al5``)}iBMiUk=#;U2gVKKgvE^*G#c2gRl1}}E zcj<1f`yOp>c84aWmU?IDdG_N?;KB?E$&mT)(*zQvUmg5pEE?E6Zr&We~d zgA**~pvyo?ysp@nb4DVDX!9$+Gymn$45~{M-r{VR=8D5pL!IR#QJzWU&n;^B;?Nv zec|MozsBPt%2Sn-UbFf(D!hvfU5CWw7j1Nm=-EwCochly#Jca6JB9tRQneO1%Z< z7CfBCYDsihEex9Y?9|?TDl<3sFM|oWvK$uUf~tn^1j@M+#!o+At%M3Z=@ico1N)Yd z^Etsfo@G>s&pj*fGV0VwfiXI?dh=KK{d4)z2ZeeZBL^;}+O7PKrx+m_k!%@AA-h<1 z=6^h4n%R!UY`{$Y8Thj`YuG-sW_d@fuP&$FZRm(g>3(qPe|>$shcK#^PpA7ImveBV zwiN{Q+=7c4;gl;t7ZbE+@oEy)V1HXm{PGvp=&1c6?v;zPR(u&BDk>^yzVcL=vh+{% z{0e#*JzmyYE_}3S4Lwbh$DIWPCU87Ux;cATM{kW~k-8quF{F!Xq~{d~x9NLfgXm#_ zR$pP34@yO@BroH~=KjMJZg4PAZ!Z*b=uECb^=cv26T?X&*9#5M;7)Xfzf&?f1OE&- zJ&zme*Z!>wcCT)8DFc-9#3McI+LaQBXyP_t%SUPE-L|1r95W^pK)(@uM%J#$pD$sV zw|`qPY2^aVGd-raG?=t%;rSm-qQowq859DEgV{fZMf~1 z`+QIU{qP@Y`BbsQM2Sw50+zRxH#qr;_2We{?DaKL+h12vD$jcb42VEK{1rdl^;Hm~ zc0*50h8G!!=f*MWacHxPl?XudD<=C%wps|~An5>i7(m6Cp%D`^irxVGEqX_T)f8Uv z-PGFJXS_zQBXXU-v?+-yR>e{?H9CKzJFrtt++erTO}J3n z{b%@d=v9vBNiHZ;)+|yfXItKL_i-H!S{M&rhOP49`{SKPns4vl#&9 zpXc{q3q9Z`;5D`bN?br0EgFc(pxunjq#`3c?lB*~#VF(In0VLx?$o0~kGWHQqd)(p zd(MLqaO?yxSG0wAW^Kslom^gq;Io+vfdROZ$;ELUP|XY%8!J>7JdfJCj+a-v*6}uT zTD4Sw`)ciZPhwRk`E?2CQjzOawF+Uep&;vIU6LxAPPN4z%Pwy;0r9#FN}yG+C<%S1 z^mo5fZVk_WO5F+HoDD@N6J$4%YNoy?(kh*qXZabjHb32;alH0D;+t}%`^}9|TrRdY zZMC@Qai+94tz+#<-s*cWrLMS<_`NBrJc??VHo(^`A86c2v=~&Q_1^|}U=c#-$CM6>ef>Go^JhcP^UXR5%Bw&FLm43L)>{ zgg;`oJMUPjTDg%U6$l;n-Bfg=3P_?*?&r7Sout7~tDu>2{Tw9?`S%^&t|G#DK>Z06 zca&-Tw8L+Tc4HB~@zd!4h=^PcRgtq1TOgMvEGZd=_XPOlT|p-sYamKpBBt+qV|slb zSyeJ%0zrDd8iZqZ)C?x) zv%SfPF|k7NKdJR1C&B2wy%(>#0f37!(7I5enhm;dKjGDgDUSJA1O{O4v}}JBRygfE zqcbs1&ZGz#2GcQ7{V*DYA^3)B@|)Pjy3hRzQr{K7p5FG_E}G3k6(M4-L^KG(i#_<^`;#3djTbPg>b)!VKhfSQ6}9e1I@1P)YQrG^jS z-h@V zMu9rr`iTteUE;B%HwFVSe4wo3?oxN~8v}>){;ZzPpxsJS=!L#N*kpER@HDK2)9$VY zcf3)BXM7Izt`v60SG5AjqJQVR#P`6u(VMuk%ADu;{D za@GUB9Z%@Q9R8B8`M_ApDGt`%XV;kJ?ZfwjX$r=dI9lIZZog z{;$XDKTjgV^TiVzs|n?w4|RqClk;d$ktUm(+*P?AaiG~4t5L(O**|{)JemwJu6;LZ zC4%HwKOFzX>%vPB96sGdE2jMIcAVuGXfCNl={8qzfW_t2>HjEyRiV1`yV9Ubm`*8I z)Jj)LNePed)|3-Ws(&tquoX=XpJzbO@g}2RD0LuZbO|!!`SW_uoYN1!ewB6yTIh^A z)+H*1DEI89C1^Uo z21hZH=tlz+26e)5i&Bp`3#k79KL6UH^_HXd;GI8{lz~t6?g*+SstDXJ`vz)sWqk!# zD^F0%o_7b6to3SJpl?7J-cBz0Z4&qo`wCzdXk2nOC)UeM->UK-LOap}!7zaoA zUz6~PrsL`riM&^+|3P}d^wVV(t80?MmiP3ABvo&RTNyuo}b$sU?P^GIL9K-PD(PvIQTs@GeGzW zavGdBAfJny1l+Ii{%EaVa>CB271OhVaFZK@U|TEBpcKxJDQGqSGV6+fVr7c?W1$LZ zz&8Yx;{Bx-{riI?TGYvMZ6uKBK@1Q7iH41j%#c_ScznzV_9c8pkubN)`4J-R{=AiD za(EH9S3;S*zN??>#l*yrT85ICU*%x`5>mrDz@Hd~7ik7;?*Q$p7@HU;b@t=n`sTiGccqVPC;GFc zF(|d}ohMw{WuL{5S|;PYPmuP=()6ADZ=d7|yvlt>!y(AAElzp%CcZ$1YEp7)ul?8a z43QW=AtroKb>=V7X+y(cAQor!pX2qO$-r2fM+oZ8`nlSgq0L+`H3# zBRlvq{ba}BEI`#v&nkH_^G21{(Cp?kM==NH<>p&8jK06*ij9iogR!~26v0R$_*gV= zE+p6qm)(uYY&1HSH!<^63JX^UK&b>aYeHrqyA0qkHdrJDrZVYQVa(Ur=6No*LWvX6UOyzP)U;r0rsof)_Ta9q7CCv}%6RJ<=6DiQ z{r;hu*Ie*Y(p|5{Os*&@#uq@TU-U&L`RC#V@ZjkuqUZf7`RX;%;U@sUQY%%*h*9fz zH!=e_r$(jq&kKv&^BtrW1Agmb>zTr}yPpZ=@hUGP4;Bg=muBnLf^}^o8mR(PIt);78#D^(KJk20 ze?~HW#$nVdg4Ii!&-d>Eagfp1!E`bZf$|rhxcv@^A})A%RNX9jJ3h>!(GPW>_x6fb z;pjDe>MMDWK4~O6*F>NMYCMqqaRA5^99Ygz|r;%%M7_-y+zq#1YEZ&b0YHMn>iej><)$2$|h z4iR=IR^?`5(f4TKyNj|S1#rGskx6gs7V3G-BxgHF4|g51MWTQOUYw1ZviHUaBgO7~ zI2iJKh3c2W=ly7oKB~YD7eEwl@2(;@Ruk4% zbmp+YdmV@SS^6k%KPC9>eD&%g=I<6Pi2ISNMr!b2GY0Hc_ML#_9kev*2|6Hhu%<8Q zK0w<6)JrHTao}K)q%*uC9CJT)FiRGW<}MN!2200;Tb|{Bt_$U}POLD#89dN@m3eNR zsMTI}dcPqlx|EI~Hut4HlHtpa>`)1-Q(ySusE-@AKp%2w!JC?)*0rh`cAbv`#0)du zR;CH=n~kt=Kb7A?$XEIHfgcjRG4VbvmkBDN(y&0B7Q&j)dC0f zp4=PHxW;BYAI_*-zBxdgmbYg-B?e-^RrlbniPwK0{^94)<1od@!GHZrhtI&6L*Cm( zH=0#`Sdwxb1k`~9cTMd#)u#%_Z?sgk&0cKAySbJBdrlLX`h-CVs^_tH^Wdw2|9KSx zF)s|YO#4Yka1qz_`8_&0`LPP#LAygvcM)=Puu@15i$6rF)KrSF*6!gY`MO+#%m~(x z+0@m9S_P^=-Rj=vurvZ{*2grwx`t1iZxTNwQ&?p2dcbh!jcBPXw|WEwm03yA4}jwR+nRM@yzlwlXA;s;qx;##{U z0?9JF_w+ro+CL|2rHrTOE~uh1-g|xEy^Ava?%MQ*V4&%2OUTff;$*(Gq7yIy)U=D$ zN^$P1yn~|$FEIYdZBqf47*b{8U~x~kK2AzH3NKE8)24%DuN}w{uvq>Uom&5E;ZKBb zBB~rwy}&z10Gta2^Rn>VvKrOrZqC_?I^3CuR0)r>HRhIYlyTI-eN zUAtMZ6|D07kvd~;jZ9W`Rd75~EH+3|J)~8lQ58S({?ovyu2K7fG@#7|e4!NA7&*P< zIqDqGIVvUPDF0k5kN%?W9IUz4`~)70nlXL~KZzp~b^>eM+X#i*N3TYY4I1vo~&XlGtxMzBs4g%5BIU)VYzm+E)$k6#y4 zTi$g0k!*a&@GAUsB)EIw4JvDiYR7H|Nc{q1GPqOJtZk3#e*EbkPr*bX@^ATgC{yM# zjOjAb^&wpuk07kuJ@z`NH@$LTr1)fz&avG#j|$jYY75hd6i}IYS9Tt-9G8(!_!(iY z{Sez}m(@`+yN41Q=@s|!PS4M~Z+ZKK6O+G+VuzP_ANQ-lU-9L4Y#3pZlk=nZ-DOfl zv{f*N-|{I%Wx*oQEyqT91})oTbTYfy4QB8paUO0&y5zN)cqzwIEh@-M<3tt$!))Re zP9z=|R#;>tZvCC1CMl_wK@~H7Y%t5pqGf=}=fU67Q`hY9(3u}{5*+ezG&zNHNr zMA%qrK4Oo|Na6f}CkSp79!H$KXGa}G74&B^zZx1wWpx|)kCr^zAn#)b#7xR!GN zTygOpEx9e5pUiF)(u4|Rfos4{{%3~&b{)Ka#xcJn=KPTM&mElwD}bj>|8p-UZQ1<( z=LtKcku>`v{;H~fd1stpVju4@Nn&b9q-J>i(6RPBe^N{l1*t3_n98I-O)uE@0GTH4 z$4oqLim;FL822RADwY(0d?Y&TO*6LiKKoBU^2rgr{*4I8_8O+3OMsWsS2Uk*nuQ*jbq>U0ZUS8d%Lw(gS2u z-xRI3@!AP}nz=BvGKReId6wQpsN`JpKZ|o!#-Ucoe>)8UOqPs}PLin5L+DI|@k?@w z=!n(N?_xNiWn$e0#?MXFAYt|j<5ElJ`BvZVk$VCxj+}=R-)Y0~lYAd8)JxAoH6Cvl zjNI45X@j75AincA@({>um3|RHlffGEIEXW;3AL10eg6fzsq*+(Dt;FCxI5Rs^O}BJ z3K?#_LCOXLfP4C7y|kCcH%p)_j=wqznEGu`w@YhSUuU3(OgQ=KQ;{JvP?eC1exM56 zLyczem8SZPE&Ftz^!qj9w4RMFd@TKbQ4KiWR~pL`2yfls&`fWN83KwW(LXN zGM7vXq0m^6FwB^mihC^S1HXv(wa~3~P&%{RBC|gIZw@gO@#hA=d4934M-TI*f-@Lr zlg|!$ZWpa)59J1VTfj1?9bEVqQP-*#&ujD0@TNk3@Z@|)rR0N!ql1C{;?8y0TF=_m zN|M%ywEXMa^8+LR&XgO;bJd;t{ZamB0vbBB+X`M$DG!J>1`o}q)QG2N^+z#L{q_?U z&hj)wUq`$V_;d8Bm|ZFOJmzN01T`)QSJ)(Hwx4VQsARTz*>}*l&a;i?)|9U z!29M;KU17~2YuWMR*dF(axRBZAkj6KXR|!~pR40DROInxbw{P|hzk!>o^dDc-9`1if^`~)ccOIj;Z%J(zQumIMX{lrL$sR`RD%|1m8JM~#(B?I-aF<63 z=;_PADaXIZnJK1T=`Yq9dgc7H+zymSCsg6Me!kjYxp zAsoPxvI>X&oF@@GLHyb0=2T#HBNlXyMFqP1-b|?<-=FQ|*)xcR>#uQ+WUxupWqy1z zKY9z5$JxyJ2#@yNy;9l2l0I?u(5@>2i{Ws5ecY&TzM`=OmrgOev86>Ol1B_C=w&G0 zO6$_X(9*%-(&1?E(AM(#&?^%OKT4h!o~Ns1j?1R*7O18u^gjk#e9&#i3dt;AxCrFv zJO$Lap$6W4H7THb3CPmLLll)6r88v#z#}DccX?nicDPi{jtA*pdFlk;efAs7#SkQU zl}*FJal`Re52vmTkCfl}szZ7;uEg1mALSJHtA7(va$;IMQ&gIr58j-k>}s7`D)b^s zmSuQ+kT^P|J5@sj!1TbW;l_~ifpaIwsL+F_3-*SwfKL0TsbbE1&^I^^aFewAGw&{k z8$Y{fM3l#cV0n5}sMWlLg@gNf+R5M3{)cak{W9sRBWN)NO+nTX#ai`NaGzfy$E?Q^ zh?e#%^3q0#bL>u)Q%0!|_|H>grO?WysVV&=^MAa4lCL8<_V4Ct|fZ?BDbYG4H7qMuZT*`O5qX$g*lt0P*ejOrx-> z10exuIf8|Xw)!thMEXUFIYm`PEGW;~|H^blA!i~hbE)AA?I9fZu6I>aQC{B_vPh&8 z-@<{-+t;b5(zluhBdK*CnJ<60Dq}(z@RQNs)lt3GAQQ;$f*SE!$IaDkDe&+!PQp%K#<4KpDW%6 zyUTjx2akD$jpf!OTKC(fg@LxY1qC1bCrjb6htJczT4(1`xNiNMDGgMkZsrP#djHgt z`KQ3gajwX=iMde2{Ldc_OM{v+vjeUY4sZqhAWODtPW`C>Z7Au<#pe#T(1KJY2hoo zCH9$?H)ol=roU7VNLn;UM|s5;aH6^mu;7hBs0#i5olTKZ+jNa%llR;^fO!vJzW@_; z_x_wy*-Qj31ENAEuTm6`?Ywj62Yl>rR&X31zp0XWJ>red4Lj};<1B{|+nhftV~P0Re#t&d+&~E{Rsh*;&whV+_Haox3ci zyQ*vP@jjto2QReJF3@$L-GKE6?8H&$-Nqq|T5RYtUQGJ!u8{ zXe!kax7YY=n`=v~)tjAno~sQ%_Pm<4v*}Q_Sk8q5kayLt@z1lVYdwCSZ_d6VXXLUF z1z(JIFY|fLHc#ubOlD&#igvi{eJHfvEmER19JOg8pB~otMR1$tbes(6H;=JaFV_Qg zThP_~Cu1oughfP5)pn*zIkE*;SmEWHXQ@UKFRaCS$ct49L-GbNXC^8oPcW#nDYUOj)sSil!s1To*XeT^85bWBHY>w*>q+xwW@O}H>~h96rpMR zi%dF#uT}8&jGH-7q`{ZU#5Qov*DZ~XVvaskY()Mc#=XI+I$^mbx%@$*YXSwawFF3U zy*GAyg1^GxdmUg7(I7}4)5n{?b@zy>p7qrUdxb?Xy2DkMp0yj2i%XaM>7euemg zW6yJp2X=ULbpHZ!Adq=pzyDSt_V*d}5Kh+4_;xDR@!Vn0tNO8;Sx3yQq)S1bpEsf%t z+7tq5(5~zRdIt;9g~6{l(A;YrDS4kRa7X0i?q;OJ?rtEd8)-DTrj&jMyK3;SO@!@& z6>H2GlLujEJ#{_uX!llE4f=Q|&b&ZL68X@KK93l6c-l}Refc&%pJug*!|kx<9q(Jh z#fQ5qk+kV@b{9BAz9-Novz0%q1|AZ#P7pgt7Z*`jy+b7m0umqmNjid)Qm26LJ*ODz zD&3^05|d|ml5kDIO4mf51oqI=4Mb?X#Kl@&Zngc9Ax#1a_pRcv`R~6!74yX94&Sl7 zFyAb~R&0{SPG&VO$PN$xgf`S3Rqd!>E$XOdzrvH!h}qXwFXU<0-O5s)O6IXyGWC3c zZ>e=VTnxaZjp+fK1=Tww%uX3tx& z>kI_&R{#D~sZ7CdQzUdsHf(i2F*s;;iKCQ|*L?i#^}^g~HF7w`aOTI-rB4BOpy(Hm zh9A0yfcqIy<$HfZyFFW+uv7;uxLuZJRN#+CWO>`gl*XeG#+`s$rj*R>khkPvZMZVr zHm8GG13q6Sss6D_J({E2;tBJ@2W*ULxF6P19!uO;EQP?`Y^s% z#OTJ2PWyqW=~ujxc^KQ8=1Grd_`;j1`{KvWxrY6v1?Zp;1>`cM5bPGU;r^?6ZQM z+`5v@P%nD7Xf6CoaCAuaSF}g{+CyW6KnLNTVtyqHBv}JO@_TqG4Wm~4wOFd|*j+B6 zX8S*1;<=!t7Go(;wNHN7X5^dpif>z+iwgcy7wH?}`&x7*H zQE}oPmC~M{7+QZ;;5CDj(%}&n=~-J^U@=_2t9<{0OnQ7X#V&E0Dlebshokt}i723ZTD1Sg$^V_^H9EWo~`(l)73SIr7N$?)nPlX zEyrB@bXP~7BfC=2(HI#Dc#;fl&v|G{m*`OGX^w;AngBD4w{bM z`9km{=<2UUtyYI1*kZD`|M%ne)Pn2Y_6HtTZc&t-nZ~_rp;%?GMt8*)yH?=qv_?RM zpS6{=6jzCMS|hZY!u*NTcMdyKzlU?|xTcssiSMl*Z52>2Q$;WvG0Kd7f9S!gwS9Mm zw#tkPq3C2znROb7$w<|Z7W-JP1!t$z!6dV>w}%A8+xokm(3pzV7*VyJHPH!O0}m+Ah4jJXk8_Dy5Q& z@2o%If$eH3AJ^Sod$}_G3hN_7@Dykh%jEUgXV!e@Xf`M;9mnhONw5d~XpR>i0YM4d zZvn@@ANNtCRg`9bX3>BVoiTc5w~H_ucJ)TVCUly`d6s$jSey+Y+737S@M2kb3F4wom zEqvB8Ry(0Jxy`Kwy8_`1xms}NKzJN5%QN|~ha@P`E#3-Ss)>9?YD5VRt*L37KvR4j zo9k|_w|t`2-s-Um{%We#Fd6DEIhU4VHQCj6io_sn5Y0 z@9gcD`z21wt~N^(KlzF?2{1@eWD?(^rz`IvbB1y|@5A$Xp2K484{U*-l8fc%Q}nI0 z5^Vd305A{@T4aHYB0d(TVfO`Z_#1 z6)3{5c{cDV9I`kq8YxLyJsw^@-Z2N+e$ubZN)k>yS-(Tv3+amq3-ugKgY#NmbS1J| zUiQtTH#c)N`@y4-)x=Pqa+A_@c(^N&caxlvF}G)5yK?;pfx`QwSZ1~+T;ZE+`q*VG zDFr2&A&kD{S32nHn;W52_F_inu35HR#acY|A^!M|g0KH?MLpR|%t~~K%*yYt9fkA_ z0lhb@jrPS>|1;Ev3D#neIr`SF=MRVbi^E(FG%Jyvt*(jCxJz1JEa$W4X`qdqa5m1R zVyXG@lKF1*yKJG4q5dvZu&)nM^Z`e!0~>VGy0@R7}J|ga$LH1WqKjX7GZW9XS8O(z9GWMo#`b}A%*SH`w-V|j{DCGHO7c`b1%^oh+ zyhDsG&x}!G=&N7TU{=>Fv>g)3xdD4>D6y4#!}-Z#7Q=4$zGh%wLlci8`uT%i%E{&Z zjOE{Ufd^P{!%~gf74xj&SpDmf&Hgz?J&(jNtzkP_CaH!jduTR$dJ_~&g6$IMcR85`63rDk6 z(6|aUZ9lu}xu|veY3pXu;tqpG7QQF>B=Pp5!zHD!8xCo8Mn8klvnt}=mfP*`AUccN zC{fbQg1E-{fDted1a;EL6OjsC$LK*O?pl=vS?_)k@h9jAYt00S<4|2#BM&%f#PLRe)})461P z%jBgVv(kHxcn+vyZN{Nm^8Y#1Iu{UH!IDRBG&4V}i^I+CIE`m)0bS`Xyi!gnU_| zA{g+>jm#SYgK)G_f6GYIMHA5xfN!TN6dZrX3i`@2;J{R*Jx)Ye7Y;$0ilQ5--b6bZ zVtzlsp)JabnaHR8QEb5Xqr!CRRVZn)`t?;DL#ICa3vM4S5K7;?Pq7wYZkS_}$dACr zI{TIKhxCKZV$pWS&F}n3V7`+p)D0j>MkV6yjZ)1wI&t1#VSL+Ou8%sJ$ycfs(mSdo zbNeS&pd0K}*-7!jt5+HbX|%61x75$lIMa+i?G&~LHh2v9pB=Dzp(=xs zypP*tI2j$V6f25-#VLb>g5Y5u!0V@Tf>Hu%rNI&zs9Yh5`?~Tr$c9h%FBBGPr>A%U zn6BIOaF7HYf>c_3@iePX^|O?2Tpdxo_a=8)KW+o@MjTSLp# z4E&<~VP&JT$b`HvbKfT!JaCeGbwLQ3nJ~}u*4|GXV;(L2wir7}+*`Mtq@J8rYqZ)-AdHo82NBhrk%0dfD&(msFil(a~p-5IAZy*8w zdhK7k)itX-w??#~+O|AI1Y^&>F{lLr5|&2VLdO%-b5z+fb){8Pg|izaVBsZJb@q7p z^BZJY3fw@K=Z+?=C{}G@m+h8d$VEad>|6=9xcD;-g5F%hNpb2`3i=8L$0W zdCEHV-=&wmxg-=p( z9Qi%_#i5(>-1V%DCpO-B>U~7oF2`^Cz!Q=(Ykp-4@q{?J9QcLdtlJ@PBD0!^yeI+n^`Nd1wCHf@_-I(RTfYOu5i#zuHRs z!!s@+V#DbE+L0y0qe&ex7zsB7G#&A{#HBFlat|g6bg(=Eod)`6a-sRfs?K6rUQJNZ z_1wEaE`wiR_|lAiKTn!2ekbd}LuCRWT1BT_PsDC9JIHsqR1;jX5Rx|j@jU{ z_iJN%{a|S-{p%+__hT}!Visnt@b}koxH=?uzd6mf@*1ZTZuhYinyvd!c(OpawPDFi%{u^rP+Z#f3|Xc*JwI5sz2W&RLZH zvzXu85D=g$+Ij%u@?aWm!@7&kX9vblL@JzmE3HZjp|)IpYxYBTj8~&#hWuJ)p{hGz z^WXL8b7ue<&8W0r(d)abvVFm3G6)3(fXU$n8}uF~y`aK3R~&Wq*Hj89JM~8_+A1zH z9HV#REP0R?XIvZ=On2GzBPB9G>T4#!WpN`uIHQAllMe6A4r7N7kq~`X%3~OK!IQg33|AWxHOgSD%k zdJ%j_@axH9yqN5N-P7C=pt~^W76uURc)@A*RfVNFwnH z7zi*Al9$RHycDvt+Wn>i;-4*uI^DsPP3Y5&byZJfs{Nr}yk?atEAQZUW_syE>tfr-GRcvbk$vr+zxfFI7I78ra~Tu0xL2Jw{jDu~rCUR-sMv%-PYI zEZ@<#udzWLc0yyS8`Fwm&FkX}F2s+D>C6ac#K_tVYCz};X^F=8?^TnA7TgaK+{n~c z(qZ2g2A}xzM@WpzWS6o4^%cHMZ%K{aNxva1cr=p-b!EEYswiyC9W-mb;H4v0`RrUq zB!(^)iIteqBMgm>7=F%)8eMFg?+J$9>@0BpUa;k1fgvF|wsXN&o808sU<6yC=3F6c z;I&fUF9W5?+$+=b?tMI)HfC$9j!pbrCy0Ed@ttcQ)%G`nHZl-P(Q33~P_BKr1F-Yw zC2wGX5WQ?BFI>q?QI5M@B&XlyK9^#Sk(k_wa>UKs`nSI^>LGb#$YG&JU3nrorCbdxvW*+Ns8V{~`6y6UF7{qZU zx>zB(`Q2fuR_BAQXhOw&X;ZoKxL?lHhy4(3_V{cp;MYLx@x4(~N*4e&k>qzC)ke+l zt!EI#vBuNxI-Zt4yV&a5ulw)Zj>v#U#~?1WfoDM_!k9ha zdcJsw_jq?CK&MlbUBDzi3Of)QOJpivgTdL0E76o{h#h64`}r+>ekP~uJ|6qSkMK`v zvn~EWF6f~#t3BH1`haq{5;|i;WU%7feM_r*d+u-T4if1gOKvpnqBAV(v9)$(PwHAf z`w?c#hH38O>%J$3M=kn$XGlzQYaqrh_v8PZi_a5qI_&YpYyJ2&9`Y^&j0NWE zwvAV7w1EgG8zS972xLR*DdYT#DPhc06e)26fz+mVf|pqnt5P{{rc(;NV-Dt1Ua)r(B88yv?k~^g=~4wH#sN-tRlg=Iw_L zhiY?@;R0u$-&1o8yWe9}C(Ky|!Guqu-khm%=*8RG^vxsm<#`IXXPBy9-uM^9;s;D^-e@+p!1oq<^zZyAmU*SSPJ~rZQTql?72` z;Bw;`WT=z?KX|BcEL{z2fCqDm#|#u#zb;a)D>35Qw&c=Z<}Gn?*tFHsmicBZmF{*p z3!TC3q8b&n(oxUK+z-|>^$m<RoZMD&*xQm@Ygv11#+eJ2 z$pojrNvYNS1zL>*od)7=hL}@k4s}!6dhK&!jPCDb2it%Ub{&f!C}O|c3MnP#v-^{V(U5+?0vxY%vX>f7K~1X z4vS9vAQVHy70lJ=)~%{8Cm{W|awrKpOXdO=%5x2h+^323 zF1vLTjVr{i2%A^N8)p#!=b?thpr>246D1f)Z{K|)dxkd}}}nnO3z zNP~n(NOwuM(nxnmNq57$&x|wke6RQS&s^7_h@Sgid#|-VB}Wv(bk{A~O~pdC1cW&? zF)CIxcif!3i#&vmLyiwNi@^7#=|x-x+9RPUyfeIxsuS~r@nv7Y&3;LRMv zXCNY%Mjwg2Cvr0B_50V0SIRwV`db9wKw49wcBY6IDJk!z=9vK6qUvR-YyYzs)ZlAq z-5&^52dSE4*mPSYjs!fUW&Em{C5QXSh0W&N8I#^uqo{?0VE5r1>z~D*1JtQ8iHH>m#+r+@ziSuZDz5kBX5XCvdw3+@)vjB>tD~ta#|ZH@ zm?2C*w~_yZUq6zh9EnOu8&KyeJRbTuZBv z%kD8iT%c9fvOvd8Z-2h4ZS?8#sRH2+4+~M`+pN$TjxP#HnCH6-$So}`lkc9@w9gWT zV2TG2splyYMg%O^X);OGbc?`(X&$ywEwqjsYkQDsk)&7a-HCK~ zfu#~Tm4JF!XFXHwwlKNVgbgdIR#g$U6g6rhB;f3fp2BI0TmPbc5+$I1cbUlk`(Du- z!YCPZpSyLUdY8Sz7{s9*kk<~sVMXR~wu=f>YL2t_!=ex5E_;h6S4g&_rGj5#JfsHO z(1$j`g0hG-nPj9@wD@)Vq7f59I-W^S6k>XGs((N`&Yb(|7-5(VY@*D{8_TKM$O*YX z6ACcqV}ZrnxCT#^G}^cKh#60t>>MXCsm!Vw7Hap*tP1z9d)%o=duRoJ>KQ63f{vB8 zSpxiLoH5cB*FJ=w>O_p~{4<{h{+pR{{I1t5D6KAU#=65lhj23)Yv`!<1(9*ql9+^G zICPQ{Mj<0Cbren6CA)>&>>px(j+fetG74M%9ZZvuZ~o-a&9!O{>w8++S5l1@dc~Kt zB-qDEEn>Kw=SmJ^vMqtw;u1p zvwq`PDYK^PJ˼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

  • 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 z zbbWv6hum3V^)B`xEf2k5>NMuGD2+YL9?}>|R>v-_Rv4Kez8KS}{L1iumX4NwGdT@i zwYA~a5?asd(zdEYCdvmP&bEu(oZBm+-*)HP&<>{sd`Oz=9umr1Kl7l9);SK}nb0x` zb*N#U`gV^c6Bwc)A#-q!U9Z5n8-p)vmx`P*AmX){2*gMRKHwdkDk2dP_gWS&+ZbG? z6RxxwkW`53(!l4cKFr z(L1$L;E+Bmn~P9BIi6<;>L3$JVwvt=h=JK^9wcXk4WYr6Oh8ziz7m-CzDo@(b)Ud1 z_W8olxK-uZ{>cwRAO(f;XmlVYI&y}8rO@U`$&D~(~(4^$5wzr~>`DFdwGu;m%8 za5MbbqT*BOWB!TLR#A>ZgWp@d=me^>4`I9H&;=*y)0-tGAD3LYXKjwY)&;ap>p2?l z-z?C6g?nSoN3(fytb-})@wrnYN}iiXPm7tfyRmBLew?fL-B`MMa<)JkP>734i(HoR z`!wBE+ibhzew#1ut=|aT+^H$-V_*5c+EeQX9Eq=_@X-F_nM?T;ieiM%C`SGAqtc00{QxcOs3!xXts0 z)8BO4vY6nVQkM(rPWNTxoHYwP)k5yt#Ui}@yvkOx=Y@nGVNYjg;=`V<*y^}O9@S+F zJ@bcnZy(8KGMV9Xm5GeRki+Q`p9-J%!#|M~tZuq+i@GEOXaAhvtI~fXXtVxHQFcWx z_ZWbK|2CzCToQtcE4N3U1BMKfJ0*uZ0CTYGTC|a!>ie7hob-b0|A#!VNo4j}C+<2_ zPX-SsuMs%|m$(zG+{?&4 zd8f%;Qc0aQ@H42lbL)&3b7(vq9`oc0S%^-Ohn3?ozn}XD^FVoQAvq{mepkPPSs~lr zC*2%1qgy8b0w_V89-;YwO-fd82!Q;^&?avqt;1{)gE#`d=oVdsK~!C;xq z>}j6s1=BqKW zZUel?usOL3DU$isj9Tk$-+O8X5i9JlS2bJ*%Jd?IjjCsOT4K%Zp&0)7HG&cMyM|-V zd2x9OS(N*cdSD}>cfST#krtlv(fJk(%a)ePst#|BKJgp-J6ODcJ`pP#q5!?Ab~b2q z(bO=T>FnYeEuDB;$qEBVXzH9bDCeC*Xs+(aTGeUYk=|AZItXWxE;aL{V$fP|(v&_6 z5LU+K4X2#@-==1ew2u+)_%#=?so5FcmA9R%G)+wumxZm@#g1J`;1em|C z{*pyd63l-1y zqNZmyn+nmxDd+_hgV?O#Ml;ovwV@+RQP};=q`}?YFKOb27gMz&Q+L#klC3_6pmM>K zZ+<%#@6G%OEF7Df$oG-_k*b$c(&m*$>Y|GmoJFqg4WwrJh4I4^*KV0nJSW2 zg2BvD?9TpmBwz2Rdc2MKBrn~;HEjJ{w6nBJTFKO5n7FY%_SipPkNDDnA$|jZT}*F? zr9*Hq17_bkL^M0{^lRuYVBKUXUv!QM%!v=Kd&lNs;wsj5T_%=L-Tjl(Zdjm#v}X_Z_1odbsf3 zjr&(x%gADvoD3^YUsU;t61n$9W*&UEC~k-9)Ft~=X!}ddZWt6NHzY*{ley{*ZA1_m z3w0N-`MmTzADGbitqC;Ns6OLXaa(4wuK$&K8!RsM&TX)GeO>2XU#^Y2af8J7G<#S4 z>`ON%9iOM4e&S&YdEC{y83o6F^pB#vJEccVWsWOfvx!gamM>-JO*uBOa;KSg9G6`| z0|T5+Q6rWSNkWnkIoYy|qE5VRiXs~DARQ)a-ffrThs4jk>XJN0*c;1)jMC+ev{xyc zR@F5i$i+G<(d|d{ZvJg}wW+{`Bb2-EYr-wNj%{f&t;}s`IJOo%CzP*H9}}e|>d|d* ze=)|9kVA)8#P;bNe&yP4nU_1N`+n7_Q!3lvs(Ox^$XLY^snD%Qs}pWHiTlfc_J(RY zVJ=ZKduoR!PHqKeeR2q=^p6*S+FKp|Uq+g%nLrqjmQCr{{wKS?FlzxiCm?3**=qp9 z)*nZxo_hLLAQh4-K=Z%Ym7jd_8+XYb$6&4@Q&qFL)p#TW9!tOIjUn{Q2dV_%+aj5= z;L8pACM}_fql$MdUBG71yUV0H5@&7Y5`xdlal!a zyh2@+OsJOCi3bCUW~>9=do#^Ewp1CeWUj4EH@+t~b4|ofzU;)kO}Da67fA49*Y6Sm z-}>NL(82uav8T$!-07z)zdMJ}j`OG3h0iC)hEBDZS^ufVw{BEiBfMq&1c}0f^Hk@i zt>yCUg(+2YUg;|x1XG-f!5PjmxOq|z_h>Z!9t>pJ6Mq+big2o>v}U0CRaQx=K~hc= z>|(|VkpG~`H!OtOZ}(_844lJgIGsC-r532?@ivf(ve2*{{XWbqtvO%2nIG5oG~*7O zOTxzIHb>IN;-F)ieTM!8zPRO~C+w!0Gb72I)J;RAFLm@iNerxU z6>wloKdVCgFoF*S5$ci5QGa72me7O?cZ9K1I~o1Pt}`ss-iE(ic)d#M_4ecikJ3cxOM&&*b1Q?DQc?&)<6ar(EhpQ~Q*|9|Wf^fOv^ z{w~>zc+9KAe{mnZvF>R@5oRGK^(Xlxb*=k%g(U-4>U5|+6Z&OcMt$oMvBO@n<)%vG~hv7_(44KYg0 zFl<#{&siIFb_dVYOm;~uyT5H}s!N|NdTL#$Ws|2_n2B_5+O%DM@A5<>wV$F>oM+I` zed-o3Gi%hBy!7js_$ymoS9u@@2I7IUIV6sDC01Zf_{t>F@s;1aIlNRWA5jErXo|wQ zuT}NhjC+?v?n%=}9YtU#B=PstBAvK&9J6oxw!|u&^PGIEbuw#iJlHte5oc>9R***x(~4{2iwP-cahF6%I_7OpqX~r42@xW zC{t&9#ss?U5eK3_%7(A6NGLn#?b7w_UCDmPZj}wRj!s9^A53Ooe+@i>T3JnM+-!3# zz<6EoIBnA4Q`(L+K7bt!$*2AIr)s{@trRB6&HPf)`uMrhLX@&Fd;Od4i@k@w#pb$Q zy3Pk_Qi|&&D4Ui_RHZN$092pCp2JNh|$Yp=zvVC=QD|tM(dXVR>h^_2UwO&Kj zK)8BwXj!#o2JNifyO&#zj~t#JPx@pu9-PQ-5#(FQp><7G=T990FLj3sx+d8o{Ou-t zi+FuTxzGLrmJvB?XJU8im*XVvv!A>A=Y6SL#1ea7B(_8sW}@5N&=-L^$zs&=B*IMj zQ6r$NBEIBXc2Bu`3N5^}v|Ig}-m95;J9amg)+{h2fTxf4KN}LKney|r$<3UN!<&=T zKF7#yc78zo5W6q>+*5U#7rhfwj{TF)jMRp5m_7|X%COTru6~grBH!&LY$)h>lEZTj z_@0t2mHCoRnNNJ#P5%5>D}c#Wz9m2ax@-)&+z&x=FE6LHv=CuenvbgC1`zst|Mg3A zU}tEjx-Mn?4Aqzr?r#^_%bL7|LiU$RAm>Gl%xUz6%lgx}hkw(Bmo6xK;`o%)Z~@cW z&jP7uWt#NJ=y@(l$e3LEzgU;codMi}<>KgwE+o=SO8)9HBTdp=8|TuL>h)rdiNYoG0+$Fj4j4xcj0-aniJUpHmobn_Y^Fo?K@xYJf{RUC zWk-rkSxsF5^JewxT8@L>JDq|l^aVR>r+@Xp*kO2<$RM+p%9mSdsSYL`AQsyPUL76G zK@ly<(LHvo8RY#_TFCKdn=a{|QFc@7%Hkgp;YV(-UkggM&8b*?6RGTUj!5>b&^7=p z&Zdi(>!zri<|sMK(NY55OxLRv|9EJ|$`|8P9fgx}_T5 zOF<=gT76HUQCT+4q3z!y2unz(A%xDD>{Th)P!AIRCq)~ZE`xQ|1D%+9!v{2ObVCXI z&dDxBXsUj;A8`=w+Fw7v>fIThQ;|X7kV!qDQK(KR8Yj0TfI?T6pDn31!1 z@IwLhN)sZVk;Ta0&=Qq(LjlogZDvICOMwHRm5bTYf^Pj#GwrWf(nTUy@KpipS2`pK|0tFt_TpZvE&*~Gw*1T~cr>i34|k=5f-n}Jp~Knfxn zc!7Uptp|p+#qjlMrZC)}o!p6QnqrAVHp$?Ii=HRFa{Z0Xi>+vUj71OU>{KSV9gPYn z@;vb({qcC;SJ^Vf2p>;I9Lq1IWRLwSU}UfbB+T{?xj8WjWQ`YCNg%0HDcBwCGMYZxF#a;nnJ2gfXU7P0tuV1OE zqNuw6<@p9+Z2|ozm7ERac6jm!+RGLe?X@-Js?C?-A)KP`jzJ~E!lKzc0rcQGq^-^1 z=R~Q1>sZdN_nHr`FZ2AFEa@WD@-1Wh${P5@@Se=vsr&6V60<4vhrPpEf6J+4f5)H6 zkm~s-Qx;_bJwBi8d^$vpIQEu94OCk5)q=Nb2@+>T?+War;?0@MsOKp;ErIK& zuf&siJM!e4mg-bo5sW>PLD`h$@dU5LS4x>Y7KaQ-n#_50*`=b2kB#?-$PhW}n_1aE zZ%F>f3-Hk2_U_Fuj4Jws?S^9CpA~v(G)B%gAQ5>J)ef+{SGxv8bzl()F-F_;qY_D@ zx7M`^eg@i45ii^1H^`N0k^@crr%#%=&2;Ig3bW^D&K?f;!)SMT$DPpeEES6Dcaf$N zoglV=4bD+1cm<)Hr%@VB=%q&XXdu$=?Tztj3U^>SiBBxeEU49K5l^_${?V%`wH>Q7Ak z`qiD1ba|QzjC3DklL@w#P1&Dk-g3lErqjMIq8{;D1xWoXQ@xnQlb|ZsLd&%hE2glf zRS)IIv`>3~$p5TVj>w>jASj|+US6KQG5Ts?;-RtilRNNAZZD?DgN;GK`yLron{>Cj z3SXOl&w=}^`tOgL+0s8epV(LRES*=Uv1XfZqMmWrV0hrsY3}p&8eJv=DY0J8d~5y7 zw(`N2S4MWSx#dzsr4~lc!P#F9eY1cW4kcRGOSyt0U+MIC=>9EC0R<((cNbt#wcvXu zAlvAD&QX>O_XlnFmQU$Af=$aZF39&S{1?~^?m93F`gE6CVCx=*O_WaWZcAEk z8cy6PI?eB{-#dO+z#4Me&h_5DZmmwf2ow@qQO6@PGd2Qa&3l(Oa=TWwT_ZJOU8HG3 zlXa_MlVw<3rfRXq`s5~(Gr{u;&U^Rqak?6MUC<9vS+NT9BgR-m&#l}PJE^b(?^gY{ zZgoS)mI{COO_Ee@y%p(jOe*rr9&UK`VS$g%rVhNe--DWvnsnM}jBhSpILVuowc}*n z(O-(w7?YbS(yZun)99P?9cMq-jqzk%?pF`oHkE2#Z8{5a!&V1eSmGB7FzBU1+G_o_ z26Fyp!l?l^g$dl3DvBAY_<0kGgE=M^NT@t(`&GER`ERtl?~LEv3?0^jW_AtQ2+a30 zqxecz9QfTnY46z^hrvxNCB6xBpsZu0v2kNYpS z@{l=tb?T2!oMN{+N=ddi4-UL!EmDiys|@w^Lo+6*U|V_GX!`t1N6Oz0{!25)R*2>^ z`hRdm`lX864=ZH>x-}G@1nIh&7zhHUbm{*B;Wa)Kf_{5<^qPF7^n(7s8pvcN2`P}8 zKP8;E3*?cdljLN*MRlFJ#J8b{R`3P-)jC+1p|sS6@~G*s?ISh};!E7pzmKh3FLnc_ z+`3cZ+_1ZANroo1e>7A5*#pnL@lmjbcC_od5R|ZgJ5f=%P~uGoJDxvUX58goVD?@H`HV$H#44Wn{H_9v_ar(7uoJU{z%$zoyK=Ct~uR zCzOnd_Ifp$eyJCfy5nNv;?zqJv|JfZ)lBL@EpkTD!0*Tii?@U}Ay$xpmG^B=; znnO1HevnOg>|&Es3`!wlPwv`zl!cjlL_egPr$>vA_V%o4difA*xLm`QqNsnb&mcDl zY^W-7Cw@feN`J9i-P(u|kUCkofylU8M!4C2)GApT#<3wJ&*Q?deZrd(nNwXgO#{k2 z$9@#9nLS2Tqb(<8N_IY!6nr>rdW*@67F=s(FZWmo2=TK*eK6zHY^NRJ!}=whjHB^& zU6tZs7fZCGiHV0{NeG`xCoTHM^Jl$H@2j$_@eOWSft4zTd`W7pYSGQ1QNa{ekEVTb zk=Q!APV|P@w~PfRrwpYN@x)g%pYtp4lhooKp=E+_1t{uojRvLS1D-+lH>JlPFkT&L zQiS!Odh1rdt}2y^ML6l+?Yts}k+z}pYp$qx6DU8Xpjx6LLaKiyL3CY!wf)XpMjkHn z(eEW>rMIgbtdehZJg(TcW7o}cc*80>axfURHvyl5xPU6#gS0FCpSF+aRQxQ+_#R&i za2(D_ydxJbMk@W%^+j*)+?STYEe&$pv8ngOGs^HF?j7rv7{2#b-eRUDx#|OFtyxPe z@R2$EoG-C!h6WEy&M?xJId~MD+^#fr+G$1@B0M!M&JnF@Sq{)LMbU+lW4DA7j^oJU zfcDGVsxd0rIp~$)OxDgr&UU19M=8qVF%#M{LVyTYs+(UTLe>vhQqsu9CD%H+$O;njY70%0pm~B@}yQ#`^*Ist^ zjik|{$17M?kcM0K^{v)dMO3m|Z_|)<#un^De)HTb&oKQ(gzihW_}eHWUUx`VW z2B*Ng8~x1NBQ;UAzgKP*wHqms@=Bh`M&kXQhjsWJCUKs~sB{riT6Ah|%aNMph~IY8 z`m~n3Uo#w27>3><#$?}DR3YW{_dQ}QxcVz~>ze}$`Bv4Sv;Tpg6AwW~>L*0|jo zyF_4sevJjv1FgvZ2B5cz2HfBOosdQTQI-FbrGw=4(&%&Rwl^5jr=op(-U2#umsFpE zJYd^6v(+<^jTP7#&WNt#xtN0V{(pmu+3IR^-ENk8?E_k5s(SYP<6DvelE=>rftKF< zmZl(FDa~N}5phdr<>m^BN44@N_9RKG~gJkC$O@ds1Z{qg%rzrHwNG{7?}Rv@NpKKe#R zuEkBqNx)z}7TDUb9AG-B;$4`bmJILJlsHnAZ3~FszI7^^`SGrc=pFD#)A`yuR{RSL zxTB$V3R*7|gzLWD@^B?q&&2y@u|m^0io)4`ejTKr;js`LP`JfKhe8;&$-S4lrf_;WHVETWp^#qoG~j*`8^` zxzk~)69|;K%g)A3{C@gvhC!T(rK6IPL-O8ZgbAL?uXwANMM@YZkx+7YQFEUG(ImUC ziQlQ$s?`|R0EX9gJGVVjed1o$esQ$+mJDxOBNVi5zu8R2&YBjQBcCpK?!WLu96Pz# zIX%!$I!9(-MSYgG<4;+-sI&s((o) zWRNZY*@0f+Alv$vY=k!CnO)bE&2@N{P+LrE?IR{V`PW`G_wMvBy3L45bl+Fl9>7Kh zbeiD3`6OUW)u#t+qmPW}9H7vzj!!$y=;#)!uu?&l>|-8Hdmx=qq@9C<&Y!EJAFUY} zOZ{=wi@Eq)f!lO^nq0+;CR8?XhUb%gsm*58{o~dZ8Nfw-0J@nLe+4VaGa53fai)!$ z)197-6Nryz?pK_w1O^vIu}Qu5$V4X(Q#Fe~iX2DBM$v3+_Yd7>idY53W~0MU^S%cX z$T(2XY>S#mYEC44{YmpoO)&1}@#)3U8N|7Hf77YnUMt1Dv#7R1V=iT0DoP2MdULH` z__co)-Iwk|KVthX%Lqi*X7I@{e0Gte`2-4MGCL-M3$NJ@xFq$_Avk@$PgE**zvk=o|6z5HG#vOT2$M)PjGx#AF#xt5V0yWgk@Dj(+@eJcSIS?JnKI zlg62od?=a+wfl;IJ{0z!1=>|ng_T3kG-B5TPYOQ2$!_$ld@MF|WS8!t>tCmSLTlrv z!K>W46jtU)2Id!U(%s$C^!+xOD&bSm;$FfdXwFo=Gf|pBHq>a+HS=Cs_M0~4Olkw> z%#Wdos;}GnvaL&G~1fgR%Qo z^GVG~SI*4Rj334vHdJ~a3|__TXh<_Px=+jql-3_&T{+y=_;<@;fnJ{0^QZ2!;)B1| zm}U^Sw@O|-YC~4OmFko6$z>OaM7A!k#zHT*?Z3Q+|E>jB^9g}FR8F>Fsu!CVQn?!1 zjsK11!4ws5am|Nr&~wq$Rm5@r&A(_u=nct}Rju0V=V(aZ{rz7&0LO7={XZ<$f_@ve zdL911Wjp;{Zi0g!|44SA>du&NxwPAg7-xNW5)`CkX4-O;LO}4P@3Hz6iQ+c0BfE!b z9|=;5Ncs|KH)1pNCc=*Xkh0}kculpwLK9t;tytTS=|dX?=Z;)=Dgr@3Cc4zA&e8-G z33i|>c3k_h{9?nW7=(hH{mwsWK1X+B@y)&i^rIL8d7+xT_wC^v#MVUPKMg8Ymclh( zK2+itYtor^hb3%5o1J)dc~B8%ym!U-{A49)~E78DQkIg6JMt7e*KUz5Pig)edl7&*Vy^THSpk;Smo*Y%Xg|;N?CI%5GPsPP7QQ z&QrAzVR7$Ci~XyPwYZnlGQiMck?UImgijagoJq_Y%qWg2sKETU0|OdJ`FMF907Sl> z_JQ{{e1X(;S2s)M(o3OQwtDHxK2{2fnmaY4QQ}JKYZqc7LP!Rj?AhY&X9FhW0?v_e z@1x*?r#G+f?L`FXTuD_SIFD*mr0!*zOuvHbj(E)iLR~i-y6!NAU|?|OaMwb6hQSx& zr=}ku`HX!w#Ztz!t@xOpB@f2*Svzgf<}ZfPRuJB zT(U*cWG+LY@V%YR>3Z|-XrfG3o~Mk*B9qIf_2C{Jw?R%77XcWUe`m`+=SitneHe$& zFP=> zM|ikhCT?!0GvL$_KT>=iRPVL(O-h)DUn}BD-YPIflYdLT}~ z^yuy=L$i~&gPBI9^IV{e*LDmX=!`Hs+cT*L*PxC@tD7v>qunql#A1jP2xSx|7aUL% zupLAyXiQ*Vj!6kcZZt4!6^0!W5+=MyR+^o%Q{?0b?4VRyvSo0&Gaa~ zQr)K2KjdJw&bQn&%Z$avq!_yXXvfD1`l7ZQgkKtf$!WgwyVh1^5iE(i`@B8lKg|ng zHzU_DXbIQV=e2oO6)*ie!qnS8bf&YZu~q^_E!=-5kK?CqW;R!n zM2j+_>zyDQ@@Ff_*hUz;=T;5$w@}ZVEh&3nigECxKD`ZZbz`kIT2x{6wB1(RhYGh( zsZu5BqlG3_S#MuA`lcSiLOf40x90mtWh#VyVsw?3?u-XR$lh*Kele5_XL<4 zFl?Cr&qxQ6T!0Yb98USGvaBctl|#-giuMksYVRc<&yk`(1swFbWxf3G293u-Sio-3 ztPN>B>Idj?7&yM@a*9kX;Ry*U?CO=k>QXi2CqnP*FU)R$Y3_xQA;!g-=fC7eDxY>N zKcC7)IMnUc=Q?g#f`om=U_dF^rV799=N!x1jmb7t(ZBWP22FE-Xd`9a!6du0yyr=( z|Ne)QxB&d4mLtaL(PP0Dex;k7GJzVY;l64%%pMc>7}&;bVBJF6P}lsG83QBFsldHx z&*YVA}qRk&8o4bn$tZsRv}= z+W+_2AFcD%Umw1@1xH2(pTwCS(c@X0cUZB_C*_bOc`Ahf+f}wS`WEXdphMyLb#jJM zc3FJ}0)}Q--D^waqv7Aeeyi%(sqoKULy=$u_ot&7Ae)7&Eksd~d?{XSjdU?2-0V@D zZL5s5k?{k9@aP~IknZ8|VPv7qS#4JPzw+GYJPZ+8ZsFV}BDXmaNtfTDy}TFx=n z-&xa{A1bi9>Rn^Uu~jP9MMio|G4=Sc%llqojnbMttcvUo^d1);jyM*5Fu$BV7EjB$ zs$kCGZJ={pVk%36bc$in9Cf^UbnjjYF@M=xR|?>^Khk+iZ*NTwDVxH z*yHewN= zkQtx7#0ehzz@&JBcP0&G%bG1I>4lw-7b38JLqU(QR^!h(8?Y+)bGotmxUpI1gc)E> zxuy3GCC$brnBksIZRhvq_7eBP5h`Dx(uWg)#t-X1JUbK|9DHSF_|=~>)hPp7QEW52 zn}}qCk(ygf`>a!y6f!BwYCIaq1{290%>r(yDlE{qT_MbDo9uk6Z zvnn5q&(r`%J|4q^)-De_X65%-dz`qWItU1)I2+G=$`@BlK6-8ypXHdk2{)gX!FTic z(0+Kdb=94dAWu<8?I?oLGr@0_#ovv}&NDTBh3~KtMHs1vJ&CYUXN`w^T|RwBc@X=l z-P+2kH2dpYss<}+0$shJrwANLkuWbhy**6OK=xu;I(T+S>P9ES)N+C&50gc?Rf1cC zzLHa>wPk|vz4~%>btrWeFFQlORcAi6eHuM0M!50N4?pcDoJLJo1+57_30=BBs(dUT6D5*yht^r#f)jA`z%#s}cXDesgqcT6}EIpQVH~n^kg_Xu(zo3r&gm3C>q*+AXqw%_q;&VoYcr!SgJT&z?+pL~eU%|Ot4d-FXh&Bpn zgfkvHk}P`uMp_-#=D&&JCmW5fBRqyO+y4NI95e^P*B&e{Xyd0Bp+DrnAK{i%_&%I) zLm-TD3JYB0|Tv|;O?zaYh3PyVsLq`1^HO<0*7IT&zF0nD$X>$^YuhE zeSLEr0rXqcWax75>Xx9EWH5Rqd&l>EOn^bbG@#blG<1e|2+#Rmq>x#Vgpi;u_)N5K zZHd38>#a478FhxL{D?Y8)^9-8)|IubWfsxsS&Z_Xp@wzPuUW$mRa6Wg_*^MX$}|y=>=jJ54>$#c4oatd8y963buX6m-mX7CGJMHzvB= z1Z7W@!52NE|I3rQK`9B@3y$71CNQe7LYR?D(e-d$Me*2~v;MdUIG8DK{vcq8XMdtD zg#qz%DWST|=o~`aP|5AyF0eYzIi!{gZ~^diqf8yVp1mXzO@dJnk+LQr?ZVi! zd6ANKC2cYD|Cm2Y2ARyV#Wc`%4{ZdA~Dtc9GMDb~J=Njn7Ou_<5&ttW@kA1&W5yD4D-iKfNTx6L4$_JyW3}P-+I{+H&l?u^bo%(-DnYIT0wyL@5sQZAJ`WSZY1gz-^iOIXuUWN z^&t*_VAd{VD8VD+n@B>#$jDg0K2AMBf_pAu^}7djwG;NHRx?$Kv}QRpSo1rNy#4v< zQ5nHl-ZjvMU*7g-tCcq*4+wIzvsLm)Zati{1FlYZz$&pUcPDYqN_CIw`Z7*m76~_GF$k(HrA`87J<#GpPJ#UobZK=%~ac>J~3AzwZy%`?=$7N80{LUtbVc zIG=yXr#57=C$JB?ZvcFH6CS?zeE#iCSgu%m!npT&@afU7`*(hJJ)Up*j~9S#I{c`* zQ|&n$*~RU&3=Iy$npY_#f<{CETb|yvMxk4-d6`P1+!@Cm9`;MqOWm%^gSWdP9!hBU z^mbqWZ28>ivlurkOjHTJB5;M|U9M?jVq)&Lo1B>~YF?18^#N-2hL|0(2$Ce=7Wr6d z#2$Ilyf&=?PnycCu?zfo;L!92SARo;L->_jGWt&J*2t8a<3CW6;rgeAk)_mpK1Z(r zFT1oBx0wZL@bbt=<@QVbuTqQ7r7pheZr#IFsFk;IMoV7?qGT8z~6HPgOp> zk$N-yG3Zl?>=gNw@{1RrkLNK1DtD}YR+0p%m}mu+0M`+lJeecH6Z%jSzwHrc5AqXoJ2T%a@>72g4{7Ihp(IHdQQC5P0G1;3} zGtV(SJ-aTUdT z`t0Lbknl4;(tb@2#gkW*<`OG&YPU2^fl2g_)V1;l{9(zmLe~8z3Bo4)AKlz;k=mEL z4?FR%ft85Ibs7nro4l##PbCE32#9c)C~X%l?T=3o>nN+t;-BL_23}p!~cKVY3TU zfYj*qE7>4*fh&?XGrkR&YG)N>XmihjzE|IT1>Gxk0JSQ<67~Gn{zXT(D-Kb;Q{)$z zB=`B9KY}zJM4@k_EX9AdGa(5P$4yyEO%0p#4L+(!U@pBA8s;ITSx$UCu+ls{1sfGE zDioRW-0ynmF`OEcF;fe9yUdqs)%xt69La`93!9OZ+tsDS9(y*wPqc6hFo7ugB~uH! z*sayp6ACS){T+5devs?ju>b=dPo`JkAK5$?g0{eO-Y5CVJHg$2siu1@i;jfxba%oZ zb4OZqMk#ugKF7Qxj41yV*2^Orl7k4Yx;!cs`(&AnkXrX9}6btSJibL=s zr8vbcXrVxWLUDJB6D;VL=ghqCdCtr?*AHe0HRsGG!?|%31Y5Fq^-|p`87=y(- z>nsg7`Y;fqX@9Uz^b>aV#B*PnL<-%kbYbkg9wEi+D!sk+DGj0c>oI0yXF)CAX%%~} zJ(1V?u(ycgIUhdpVMnLwZaiZ4QZyr|wf)k*8*S8fSJUcWRuBE}g5Nbl!;nXT^fRQ! z`2V|x{qT*&1GlDY{FVNBv<2Y5>^QAGTj^}P@$jCiO6z^t4!rUB*4}Rl|3Aw*n35~- z?~7ff*lan~vxjpc((^s58;{)|9sS*?Qd#_Mojp+}d>4MfP5xsz9Hk9qTTYv8G6NVA zKe17Ht~fZHx}nHB{P=}KDF*Q90IVxEOV>m3Ve9-ZqnYsQ@QMaR%NEiP!E6wjc-+}T z@Yzp=D$fpb%QoCKTsWLS9VhRNLD)PKE(Svf-B>qqC~ z`~aVobjrPh3)z{UfPh^rKVVSRzh+jN^o(y?0qEgHafqk6;)_oQE&K;Eg6#4|S zUkCIkBaGmf2q6rX)byN6&^7)WZ()7z_dO-`I+k=cq)xKbn(vc z=1~Qw;wGJFc^ZF~*jqy_223O&>>+>uY7CBYX-BolMu?(avM!f>t?HH@HY(7#@PL?h zhJV?=6K71T7}97b=HI@CFV4f2X6tbm6}1k*kgO`VX_}h8L-bf$Q8y2Hr_z= zAf5Y)l!}nHk{v(@IK*N@6+WjfT#s z{N1Qt`rXE0QuTx?p6tmYSf1F}_Pq#_TjUMthD|oDcrN83(dl6Xa>TXRwWHfpIz%_u zLR^ZO!&RwrJ^R$%`@w&^lnW9eGOO3xs_WGD#>7zM&4$b$ltWhm#GHkfBJsl6m5 zMs92>N&pAmlHC@min*jZLbYR2m1npBmKEnd?+ml%>ie?8E7T+DPhmL`;s|aUw|W;EzaEWoPC}{dgIuMv` ztRk7&k>MQYB8S_%MZLG{Y$=QW^3P5?>WNteA_<|#J%1xy6fDXFX?}}MFn;ACuX8XU zUKQ-Edt%x4)no}JHf1B`x6B6?8c`A5mjHz9#^3Gg1nM6Lh#V@zVJ2%P(_I5+*~!TS zRvty5tH6hwCl*$07_`L#IoQ;&jCPGx7vv(>v0WZYQ~JBY#nFSFzeVsR=6G{|L6!en zuiI_=3mgAR{wtdN>k(stvF!?|H|5=l!SS+E4M$-l7DE{5x1#orRHc}ey4#}7@OF{1Rh_cm8&_S(6LGQO7u1jHo+nh{`y zAiVf-oh`{h?@5(?=)DMVn2vb)or~&)`_(9Nn<~$|`E=Gkckk0%Tx%E5HDPs4t09YK zrw@{~79@Mi9*Jq*;fLi2B|Q*{m$^vC3)RzI;gCms+?(Qtn|!D+zB{P1*Z%G>6C)s7 z=wSZM#|mm{~6<=#I=hD%*FTfI-CWMhUV==cZnTG`OrHatoxw&N|K_?A|cK z(;&X#Pq;r$!-+AN1XA+#1^Kn6)U^}E)N)^wJ>vzZz90ix>CxZA$(lwimPaz)KLOG8 zZsdKVm>aB2zsJKoC-kj2JpezSsLHJ^zZ>;O66qWCbl%s@H%ug=|w#g zo*tL+c9!jbP<{kEntKa|#k)7*qqsG>bNYZ9?%UQqu%dIqa9SpUEI#aT4wMc}u6v z=hCCfqcY`rn22eU!|KOKLh{Xn`;La8=uMG3y@wL$HeYktW^iXTN}StaKq3lB^Ps{Q zeP^i5#|#w;yq8}5sV5%TmU6It8GFomt3*Mr(E2i8E#@U)J1AcH!5WW?PT~m&{^9M=H$m^X3MJhwY*7P+S}0CQ+S^^+U>hhwDaWP<&Dcw z1>S;)Ai-Uc!KeAZc0SH+(yeZlLC893Qhf2;)(E3Y^HkOdqM?KNqTc}EfQd<+-N{MFV`^N1u26Oo7FZXXyZ#~+J zHZIvBTe>FZODf(PMpYr6XkUG%NZ*mm(`wiI_8$%YssF9#(0cgGT69G((SJWv`@i+v z7=QRBek|VhLyw&LWL3a31FPkZH1tJq3`o!~BdbJ)-k~cG@8jiDvWLruz0slzT6Ed} z_X@%_&0EVTDWZDu_Ev%qb`+li8F)sC$YrK@Yt>_F9c_v}2O<~cO*pB)C?pS0sey~g zf;KRf!Q8D0Rc5b-rv{Rka4{~sDIv?gchm;2HFHeUA93h(BhgSaQDf8AS9!(7w7L(=oA7+4wXT~w zOVqsy_;0q1?`J&9=c=mQ;=KgtM!+7yLMDvw=zUn(HH3ag&;E8Oh^}S`3Dr~&abtZ* z&le~AUHT=Bq@pPxY7+9QBoE>LGK-i?UpMSh?}A3^1@VwC%9yM ziAj@~4!;XG(h_}mjn?KfXfj4`(e9MjF%W)he!()VevIwb4q2DOZq)fzJ}ub|Co#q+ zI=DU|$L*ux!Nq~ci+`4QzNxWh0R??zFug<(BWX*9b}9XSfgAfK5`c$Ql1K^?gbPb@ zXdP7^#Oie&TbIt}EMaFJUGBFI@kJuRJs=uoqHA3peRsCfB6zihJOyrJa1;mT&&h`Fw3)0~$j!?^onEf%B;xmj zwjHy37&;PY`ntgm6ODKGt&Dbih4G5Iju_TQ*yZuMmw=Vs5Ym~7F!9NFoJU-2S7#GC zEJHUuVLiJ;X=JBi?HPZv9diTGU+RRUW23;ktmT1Jelzm5PTWYp(HxoIp)J05=SJ*8 zLJ9$wt!O_5QJ1-|ChH!6gVW|^znk{!FG#$tL32AC#NZ%ys4;7+*O4J{wuy6wR{BBs z`yiV3Wp15hx?3QpN}C*PbV1oOMPKtZvsJB=8mRD;*gEu1F-%h6|y!7X@w2wFL0{ zauZ|5uBz#C`oqDacNZUVY&TjA>Y}7Rl17t{kh|YpC3Oy^Sq1R#<)?P~q1-7bxcW_@ zo?1E@JyKg57cdk3<*}~?7|Zg;t1PvgooA;p)#s3~LvGBU_s>^8+u~uOstT7{nB7l? z>vSAZSBFJKn|RTw7}WdvdgV-!L45^`MXxKW<@@TM`W}B(>P9~XfPu{)J?>%1E2ICR~UghvJvKL#yR%}=la4tvY!MI55J}!u8XE_qUK55)&8M( zL8u(7x3#+BH#ga;B>b2}_66ZZKo^Q?)M$t*m&%4Q-^4W2I$xF*??pZleb4!w>S4MU z#+S>IBdrKWg5;xa)Rt9Ul|o{t_DmDKFWtzOq%5X*CriF&pBJPgah|jwxuPoiY4^bH zD>8-SXW|fpv9KFvo*ZarhR@V+Bi_lni}H@rPa#fGvHBhR1*w7of}a=bKjZgi=y1mA z6mJ`ymkn()QN;H7D+Jr37hTBh(eK9Ed|ND3ktL;iEL%IXb#->4TUd*%Z9^1p)2p{0 z&)n&6k_q{xiY4VpUms)f2TRj=*`a^IaBCiuyt{dquZMy6qc$-cU6#6Qfp^Y4 z6w|4F|N1MqskNyoyc;!9JLi;N@ed|?8}w`1GKHb{`Tv{*OvoNaJFox$7{Aneiv}G) z7O>dYjS9$k3o%E>9cSppU+|Y>6_;T#pi4DHp2GLgI3TUpn%$Hac9|oGD`#PTmQ9jd z1Lh#EIT>R6MS?h9iJ}mAY#M6rm-bj!3?ztK$pwqw*TCCeH^rW406DOL4WjerudhGB zQO$TR1ffWOGqphP3haO{PVTF>mY9dEbIW1K_{Zf*33+UGmO3EgB(Sa&7_LnCYZ@bg zRj&(4CuW+EUrS4K;G(8#6fxYl%{$F8H1+9oeT!vLIY90^&whTq5 zW)*dJcb_l5#la>LA$v&xGDS&X;e7^r4g%y6@JU`U&;8-N%Nv?nnU34vG4Id@q3-hg za=#jv{W#4Uol|q~_Bo-$otWs`GO~a|iSfe8U6x93$y)Z!1EawIxj!IL%~ z5Jwl)u?KDFMcWoX8r%b}6`X!B48Q!dge@NU^HCVm3J2Cn=z7oia3xx6aM*vBlg61d z^N2}S+oqD9(XUjYHtX0AlCAmT#czWAHVgc}hfkpqL*S-2Sxlu2(VyN>YJ*P3!Nt!q z)hK-%c3GrI`PfkZx-4l88~} z^S1f8rYUCnRE1e#1Bcy2q-F_&-K6csxRaf>-30=T&{pY50he-R-B`ndR{iBU0}Q{M zVDT=#ng}Mxt$CLR<+_l&EwRq2m1S%&7O~+q`bhNj^$wj&TmGyg4&y$K1$1RrXXc4H z5T8){r!N*34yXeJ8{Llr$nqp=eQ>rE-A{~>ox+$YFk1D?$r)pUQw9w%sER){K;mLR zAJRfwATrffq@==xf`B91^5Z2GGHihO6aHN^jbT!P4WGiinONn4a_5AzmbXf$Dl*eVoKfRpDy zs~{<_=H$^s-!LLGP%+xkS`Y8#7leo}1N#@EM#GSM$37IRet@(PB_geIE7WbG^ZcK;R-H4^15VMda){kF+;OVI; z8%rD*QAx@&(a&uxR5P)JqNx7$sU-Xz!ajTRCa5gq+39C2gcz}{985=}$yN4lyQ=dZ zn`T7}$Yn-&-1;cMIc7L0J_B1mch+ZURY$n>6Yi8)=u#TL1&dfUM_!c~VQ0K(0KxsU zFsl30w9c|l_3;XH!-gAf2I~5jo$FT?uW)G|w`x}zr>9Pk0_Z`7tfAiPU>fHPlbpRu`5(>u`;Wtroi~SO6k~HO zA9?-eFwa}7tU4b-?;kU@N&D>fQL$0(rD9)AsD6I@iZMNJLqnjw{QgeDd^(cvG;|gp z4-c?kF;&m_-ViIO{P%A>iI3L=QxZSlINzI_V2HO8CtT=(E%?oU$oqRXxK(0M{59;H z1-`3)lc_YF?yz0~t(vXmcGtzM!MN@>mm(>U3;o!gA!4tG-is(<9Ab#%;XIJK75i=_ zDLZ!{Dv5LBWG*O@QET0+s;qH@&1 zuRji)%}3u4$)c?+xir$4l3We9o<<$_^gQN-V_xfppamD|%IFS7yc zj)mVzw^$1CW1zkk;V5H0;TthtUr@>Fd~}R_JQcI)kjv~Q1PCu=XK?&!Sw7@kxaci- zD4gUEo-(rswN@xTyiu_?gaEEz5F;QzTHO`TxO(v9apL z#0`;H4!N-%26(ICNjpSuqh8dyoxghvZ|CAm!#d76S`2w+MpMbG9S@{McjOPvxW|dv zHG3ra$uEe)-?X|dYfFm|t@p5i=bBw*yZm1B)=BP7`UFvmtfR^J4%E8`p(v%>yI`aM z0P)~wlxY6MsduLDu83(dkOel{JVr0A@(Io3-Ta8K3H-YG7`pUU+TUN-Bl*!x6X9#@ zx!nb}$Ul5Z3(ef541Xg0A4QT~DEg_b`qfp*pU8(HYjnC~H~ZEohL0kU&7H=0%*uG< zuJNJBN6IeZ5h+aaF*hO$M=VT;yLgo|uPHGsh;;h8G<8JN)XPBdUF2)BFbm0Xoi(BO z6U#wr18)vRr;zPM=|c|K!|IazY+#3B^pM%t{0fcBn1b$L=8M%t32wbwYwb3^fw)v< z57w{W>Qx`*I48W`{$+TMYw#900{3Y*@``Six1iB;DCeA%@s^*0Gmms_u z*J^|B0;!!V86q=2kxp0m5)jFfLL}FwXImpz?3-V%gbajuuAu!(e8*NV#1M6`ByQv0 z_TLmkY29MR6&}Ku4WDfgGI&7U==}2jy5ODCg8awf%l>#BPqEy#loBFuJpQ^6Aj?}{ zRZ${3%~InW@MyVCzsqA7)!Rm^6x|APNP8P-7j?a@%vA4k?`AhlA3RhGb%NB2PA zo!irmx;GxQe2xC~|1LoZN@_CL1JMJQUg!0n4QX$@Wp+SU36WoH2AY|0#jjVcqf7@< zdNZLF2j?4*27NU$CSA_D57(HXS?OwaV@kzh-L#>;8A7s>Jl$|qdWJWTYnLO*qK?`X zr92skGBgg?5G}PzcrPW~)L7rZ-82lJpRKd!wq9k6XnoD=o=-ah`W0&dMcxp%TDn)f zKsyenZNl$)cAesrCzHK>Y<(%ApN*rU5A=vjS|rt;SiMVX_x-i`chmGOjw=N{9g`21 zUajr>HYa~+ldTO9x9h4OmKrt#p2oSbhRkw4eOC$KBXR8Lur5`?V9cgQFmYGgtsj(8 zgSYE4d9#_s(#1%XUONbi)g%Xc8BjBJj&nehb}*?|_Wk zLA0<-NImZ`GuW1UwsoXYp>5Z{zLQcOD#(9es`V+5h6Y@Dfp}axP%Vmbri_?$fNigp zOd{DCW~+d6_7x8}YQACcGd<%Vk+&!?`(U0?_1J~@Z;4jjD+Z+to(`O$?5wPc3|5~p z>;rE@-oMohx>6!`LUhauS9Q?2ITFEk{~OOikaxel86K_l?Qu_LoX8`ZC8fmk&(nec zQR;)BHB33bPSrXii2bNxLN1LM9gxI7VicdGcf?m02DK&yCS_*6BSgfjdin;duG*03 z;}J(C5Qj8TJuxqxJ*OpR(iD#X0Wc$`DlKB*M7Zds+GBU>^@c*z(&smet!a zT{|NJ++@cAP4yZa2y{IIYJDm$ujoXzC{6+Ceo!PnTiV0eE-5MjQEI3xN2@|=xa>v_ z^s=rQoVPR~?$2fq_+WAv#ou1|hee@5niJL}-dMqp@|OqX8a;KoFQ}Eehq*I z(Wa(AgQqc;*jyQ-G>dAEU#zVJ!h(;y$XzFr5svEr(E{x6I{}=0^oi1&8BCJW?Cb_? z0qJo?DZkAPxqd?#>aC_iV*T?3NlDb<8UsF%hBN z*T(p0C5Z;U``Q{}q#C~*9|^{Q<#L+Pyv2Kk64t5|kRvk-?{w|>=7NELDwSD7(J2xm zQ}-+IYW3%FVCQeAgP|4tsXu?Zr(II~;42v!Chl|rPwwht((r(Lsow=gHQUL(ZMQxx zCCTp2ecS_rIaodJoWoA6R75W$7)(n5bML`PClX>VOJD5sR5HJO0Vz}pFp2+R5|Dl< z=d-HO1m2iIskzDF^`xtQkd~4J6B)llm(IIK0T2fq5xg{R+7`7`HfcgmN_g5iQL}x zg+L&AgyE%DrU!5oX95;#V3qYt6v04@ZqAx*_k+Ei1oWDaj`4TB5*`4e2s>PLufJV< zUuZ6Hy3zjGX7RaO2*FwYv+qWOELUe%ekUonO%G5$)powFYaaUd(pbbCSbVhBt@Ki| zFNJEs;lBAaknmav2a>3RLHFL6z zd)XrP0uGDEYQcdceBHAHz59nmv6F#^?D9K0jGpu2({g8bJ{OEVzLw4pE9$}aQmcOD z@!hMV&8{7@q;T($z)ZI(+s+k{NS6Ipb1tF^WA5*i{HcO|h$Pmeo0m&jGcF>(Ja8TB zSm0Ub6q}^O&se8kJ61ztpZ9=!z7?>D3l0DE`gGhzDWBb_JIa1_cy0=9-x7A-2i9IjE(T7PjMvj-4dyMoGWr9BAZ9 zU&bA$907dpLOV<0lw>YT;5_W>v{H{gyTn37Fh8*>3(2#IGH;HRsIR0$GCxNTnblUI zKeFI%*0$t~O-{WcYw7;1?&WcUOD7ju4B^$=jtWSx8tn<<%)biD)%QR_NI`l>F7N%>y19gaTF3;Z)Z ztq@sE;46ZBmB22^VR+>G{m0A7*V=mdcnh5L$ie;Dx>p~A_E_LOjMc=;&1iL0L9pOhh~*th0=9NMCOeKSl|upflTO`~8DTAis1wo#9E8IXv5 zyU;3yVZ}^(F6fJJ!rB#cp2_p*PaR1VOKONPrJF-6;GP)y$-%I0>(Y!|jvDF9=qgCo9l$f$&!cGXHP} z|5?Q$Sm>uI{ky@`@qbYa)-7iL5De|u?@|9lFxUo_iCLx{uGw~WtaSyZsohQoBKJ&v z6aUq`_(zxk@8#1y*{}O;_G))ptAHPNl$H(YRYnuJB!3*Nx=pQrnXV{=A{T$T0y!Zo z9Mnz@J{Yf&cE`N=??8moJ!e*-$ys*lpB!KUfj9(9&~mrXqe8%m$nn(3%;JF^1E;a# zfRrq=wn$(+jC#=5%RhXI1j9drlHmhj^)_odYqQ;}60lI3+K{S?nXWSUrElZK?vv(@3w(?$Tx{Y#>PwymuhpB?PG?8cL!qF zwC3dj?A-Szk^OO`^6FxMo8dnM*2Aeo0UIi1WMB8Ny?;77FSS*S8JL=8MiUECn*_IlGv=_e_{4MlGX<@M`V|xl$;b0b z!TGWmQlB4s=+PT;?w}_XVQO7wX1(v)v92mf6TQ3hd&sHRBqrAr^zwQru}5FIhUZF5 zA^p2d`uAz3o74U`Lq+%6+ZY<(F*8uF>FR4?I5rlRi6srFes&_3xuKpJ)VDs3@0P^`e?s0}h zj-`@{K3>5YYjyjve-A)$6BMcau{YxiQrPp-p#8Hd@AB1(pCTMG-pzN0`PR$-q8a_1 zB3d&sv%6E;qqyC}Y|6^aER%is^IbU;_d@bk^tzoF@Ov~Qej0I30{5!)0ssDu^Z7aD zoQ^E&rO2PolWX47<9ds3q^UKw* zCb5olGj?;}wEml;omZqxt}*4N_3EoP+F&(eXS*%u?l0fCza3ZbQZ$u_&#)2?*c#eE zLbqE&gH18-#GH&va*;mU0_O16BO|$I5)#Vq_3^5oh>59+>=x(w8+b#BIkF}cLzi?ygcgZPUEDVS3?rudaJd(S_Bc;xJY3p4htDuU+^UGf;EaxfQ zEpW}D{MG!HCG!@$nx#4l$BZvu%t{M&p7tCSmLg36y`wOhl8-$t>Z*%QSi_^|_;oie z)@#)r!virhP!eviajT`i2iH;98Bx>545uOxKmK`Jr3)GMp~#qAMlAH7tiAdXJuVgcfW#{wiYIc z0DS{qdTN&*rUWXyQd#OeH@xc?kMiy%p+&0r0vepX1p3V4iLvLeSf(1A{3%htIBzDg)odZe7`?8Vm;fE13Psi6EN4e-pV;yj?NNb`^%;S4=UJ%XsQq8R| zoN1cCvRM-W3Cv#k)%&il9hCw?#l*v2tA2C+%7_SLzW>d%UEb~`KB9{2Cwi>>{8e~O zJ;~a$MIeuPPi1jN+d1_Cq3X?z#cqJXeQ}Hb>OK;M7e9uxyBEgw+*+)cytR6im>r2H z%qB$hI#AGwVJ5^&qyw+B9s&CoAg=;D?(hA7%$@LY0i4ry-M1( z^y3ixu_{)QKxrH_}$Z2zZ&>b%~O=V@5Eyc~JxWFB;Lq?nCd+ zCR%+KWW+m1@VA#sdl#1G3Pm4XVk-JQw{x?ENjJNt4!`KLB)NNDn*QS~e4uZy{&ca+ z(fxawQVRx45q>w1#mL86X)&Pdy|jq|F_HK(Yt18AAmRNmF4)bTG1}MR+l)>oQD;gA zb|18fs&Bic;LDsj%;!+s_Gyb%Y+Zi7-sR~R=7OYXl=;sO!-N#+Ros^9%Ml$EM6w}k z(yxeF3-Wlwc;cwbS=HR*w~Wwla)jaa(ffbH^%Q>V)!T38QVmZ3;G6#}kv>piH|pU^ zhxB~N3Xwcre`JHc=oo62_jg3>E1eMO{o|YzUiOpahn`%GgJ=J_>_0!re#rNEjJ^<9 zJG_-BU`+V<4iKE?_xj+rW+b*1tp*gSR6oEs!Pn%*mmWaJ9+S|9Mp>Ef=GSt%MFQbsD=1L$Yt~f{GRsh zTg78vuTtz34t7>D%K%bg?QVwHR)o>czz)yVvoDUuHc*>`;Hz!jN?yJz>KS zYOVo7lk`N45(PA;U@tOjW=w6f4=nI2@XFb!>M^>|(U#HHZBP@;=@?ukj0QMle za?81yVr2Qs4HL9VTl_ij{yNQT<2_6cHK;=|w!N(TaztMxFJe}L=%szE|Dk56<&RL#g z3^njreGCQ|CIQ@GrllX0Q$Y!XEU>M_6F_fZZZ03d9k>uilGj#ji~)SK$N{hP0@`{dr^C zN=>lcdItrl0D{vqP7+LZsJ$({cWb)`hd-$&hQj5ONWN6GlUP&avnbvU+nNHPflq2< zzU6_QaKLobqWn1lDFdRax2GG;!r!x{*Xyq{*NK=Mb%38PPXti5{puk>YcG^6V zfXd0$oi;;nH3fSXv7^3_b=996gw2ZhynXcMT=v!Gd^p(K%BS~Iy z@>e$a`7Tdp&;IX3KpJZzb4IWxr#37>)v!`Ad^3ddb7?jNbSK5l3yN+tu^4UyR;MSujxTS=nEvuPB4JAb91y{#e-KiW!s6${__n# z$=TMILqbAUlFkR3kyNiRqwgU&(5njKF;gr+!nCeK%$V%m0Ssn-zPaSH?Scn;_)8@> z5qsdrR#*FQ;Bv$bxSw;Zdh2Q_W@1V4Dwv%mDiG9g|~VJ=8qP@34?kUU=nx* zfww1|#2+1!w6a8ZC0EXNMesyx4~Q*GI6K z=CU2JFASqo>1Esr3hX$fPBZJjGc$==)ggb!7mLqm0VV~k-zz6pjNW(P-=Ds{%TRnB zH5f&F6#dHgq_na!#8t7z@8-aZ=R;yz8V=J_5DPHvht>EcqW{wd1vtOLq#duLZtzhB z{({ZtZ&RHdWC;7IOIMiRM=C@KuBqD;OY6<@#Zj7$&f_$2c?k>C6Vh-u9`bct;wd(A zvDNE{xE7p)V!C%e77j`DS7u~$wwQCXNMj9W(;sT2()pxVQaBFV8#7pfkv_u;7GqP^0uH~iVv6#_6uJo&(5p_vHJM%9Hqj-H<#R;~e5 z_YIITT5)$vt8LXjyE<>_n=rN$qH@^hVKK!;#-Nfm5v{| z23U9yInhIFz_wyo8MW^eU{1!dsL~QGM9z0Zw}D{g7y70X+^rYMgMBQKgwGVYNRj@G z7m`L>?7xsi+ZYI)gkY0=G@0;eQ+VqdLyjT&)Bf^!APk2wBv=l{De+MIXyMMx$|^&& zeZp`1dgE`}>PFixFG6Wt`?~f_OZ;c6>cjR{GwY$qa-TX{wQupXvc3LaFd3fQ0HG*~N7VVly*TzNRU+7&pYgLX+C z{r{J2xPzm?^1PXNU~qqqwpAngf0Xf?89D~-+M^o)S^LTzU5f;%Y<`mw))w8g44Cc0 zLn%kAg2UH|{ReAKILDglkr}^npAnl+S!-jalX^1`BYZ_}T5(n;27bzmFGtN@_T!Kt z1e&MV0&k8KXFD6!=S&k`l!*`swcCJs9wmjc*#+SJP5E1fZTCkB6+(J^D{rTTNlv^N zHSEh^(k!adAd|r(;gjeUiqvpU1zd=KbHx|1(-o|fB`M}$vozGJOi;tF&8xy5Rsd{) zg{u*u5BGw?MwacLv^ws#qzKt8Tb!aGJ&p>;`)}-GrDF*EhQ}OMZrh<=OMmvWF}E|6 z|4<<~jUR-eiGrBdBGrH3q`~`HR)dwJozIWmb2ipulXVE-(0)sm0!KWh%5RvXT`~&g>m+K<4ko1Z#?}HR(f6(L(F8g%S+9|^gc zI$_0tlgXSrvAwrMYkBpFZJ7b=iWeUNC|{swXrX4}fCF_9k`h!G|5&Ejb;P}m4x@Q~ z&Xl6f-i&oPC(f5la<1K142l9_<=d{?*6;fU&hX~$-(m(0^1f<=nzVYCs$s2-Sx`e+ zD6++8`~5@4>-{kT?B}<9NE*b6;33Br5J)PDV`jzanOcwIU%0tB)o~4{!c~?xR)fy?~joP4OgSLC1uZ%+O)Y-RWaSx-`I; zWhP+)pRmf7%JUbj*1P@l&;X%a91KYf%(KBGgWhj6)es^kLxwZn-A>)SQbLoWbvHug zyd9z@MJe+2d`9)LxO?oVK0#?YQ4YL}?;^}INs&@|$r1N$>*(NVo~*Mkv5W|@Jmxy< zX#n-@YJ~sa2mPkqD9(mGSTT!jwSaP|b6P5E;T?b2DsHd{tMuI~$iB0x#YgKAG6HA= z;GVr}S?+d~$Q^X)dx6fQb$7uTvkEC++#a>k6)LhnmIy)<$+v3$D@IoS6pj%oe>WfN0-iclTO~zYiC!F3elR zz||_%r*!=O2+2rUL63?cq!?PL1Ib&Vz+y&wX0qif!J~<#mCL!tl*O|HL=T32WWWb^ zliw!rhP}KlZ3&5=WYD~YB1IB|8WolCW(;S=&;uHPyUlM+FY#-ON)*~+_pqrB3#ox6ljOnHr@qN>S$pt|6u}lF)j6!KqC1s^i6`)RnoFUn#tJF{d>9M#Edf=a|!# zzI@E;y+FeemDAxKdPw++3s!fj*8g%Rbf`-{`kEt*64h--?zzi zOU{}c0vxsBXD(Xrv&jqi-O3@YZ~N1AgM>1z-?3e=fVVvucKgJ~XXjWXi0jkkq|CZ! zpPY1<50V*t^pfWC{6n;jW9|RBJq-sFvF*KG+1`BLj}CrFDfo<-?7DcmTQ?BmqvZ84 zP~Utb(8`?VJgK7K^@?%O@q<0VhfB@4O%*DDFpKEHu~+6R*cP52|2-na_X+u{jhW#N z6QV8{Po#o7qTrZt`x)M>E9~>n4lV92jHQqQp|hfBW`y2{a~}A;+Tu+n5l{;f)S2~j zF&K%jF~3ph|K@aE0zB~Y#|wX6m99MEHb?UQ(&ri3lsvQCfQ5Opg!2vt54uo0z>3(% zNLc%Cz9PUiomsU}m|JmGJA-RLaNBVt#qK4!#tS(B8DxaUprG z&jtSFB~tKtA*7pnzp!^Do_GY_U~@5q?bjcK$e{Aub0{Cp*uO>>Q9HBArGK6(|5?^8 z#{baMMa51M|A))2!%`X2zRHs)nrMFXe)gZ2ZX4zQ=q!D4W`mt>SG#t|8P-n!TUu|7 zulX5^$I@Hj)6mdu%WO!kHsQ|B^Xqh+TXYe|SOVJrxu3RXrrJ1VVCf_3*Bc4n zEk5DGoRhK8?L=3h$7XE&qUC4_2kM6GXmq5c7YbYYzw{|lPi0E>>RTgR}< z;KZV(^fDtGx^9K}#BazrF-s-NOPSNd)}vqOX5L0cR9F3e3v)E^3WK2x9IX1vud6f^cw_Fq!Ro>4yzx4C`maID(~=f#nX0{U+UK zXrl}YXn`aa1GxIc(#a@>l0Wo4{!>S05$k~KS$i(uq_3b-Yte?ke|bjWqI9#H8C?Ng z!6-bSfB0Vdw|g{oJ?>7NK8NgK2tyjNI?iOdkRo(kzpJi_HQ>(4ulmWeD6RNGsCx$Y z%N8wU@T}0{eaj$@ju;?{gm}cxwMmC_&YDP=2LVdX%}B{VA|oLQzgfRwBY}m?{+ylK zd$(tEBRPVYfi{rFT02FH*sZn_m=fVC++c$be9^MdCpy$3D;H?&i(|igrd6~<@xED! zLm4gOfU4M8b(yZnnp&eBZ1Dpn;Uq`nL8xZJ?T)O*O%E_QTa|Ka=ub-bi}?`p+OzlI z=eo6%j45GQ$cyi8#7s^1X2bi?#EiiD$Ok5oe2sPh* zZ&gMpH9I@-3W%zdV2&f1e@u(9Bi~xIe85_{^C=h8mIve!%I<^C-xD!Nmv->>wSxKd zi3>1ofuU2lqQ7&i!Xhb_!#eI8-p{}6jp}%Er+S@8J~Y=v;u-*2^@~=dVJ}0=?M&~% ztCikY@X%}A;!#x<3!dO%izMhK;3f#fSMXiil>s~a^}>)4?n?FtOeou?n=sf5-a|IF(xq~4 zNVKi`F6YKC%W6C;p#bx@ubR72<~MyD>>8|pk0`GN!YDwli|FATb{iSX@C%RcwTkN1 zqVIdtM@~dU*k2j)n{(+wq2B@LUWzb&%!Vi>IqmiKF_kBUS|kX2#>kk@iQ%by|AZ|^Q*H83x!OuqezCcZnZnt4igbs2iFc!0D}|8Ee`gdc-ZpK$ux$m zhhX7JQ;gcBR;7@MWmxfd#o!i3(E@y-nQB%I4)43ChRh7kp@z{Pxc76vWR@#Nxb1kj0WQ(a zWwF)U?3*Dwxz*cK!*3A*3WAufih_?KJ~3^PKv89l7ArOR^}H6>=wk z-3$}kiD_}YH{Rbdn=a-*Z^U1;_qr8eb$WK}60&;ByiEV0vnF{c*Ad;ojZfMCKlHH3 zI0)T6i>iOOJow*gj6Us`nV{>jL4}Hw&p&2rlU@q$x0;{+U-J?E& zqcYVeU#8L7(L}DOW9zqcjc1;y3AnQsX>rKt3lKOzoh9G}=&-G6j`4+Y^n4PlWp2=5 z2QA|2+Px{#pbdUYlQ#%B%a1^p-wzANtl{yl;NW@oky>OAi-dcrisT#%U|SU}V|O(K z13w6f@dTeL9h(RS$0>bnEx|}osSj24Qu7RG;xnV+jCXpW4E6sSggk#tJ2K*AHC%11 zuU{~`j5{khMAgLF)+NlvS1Dr3r8Voj<%Pc*(`fi!){` z>`Q9R**2O)!-czv1`vCLwLXf9o7rAV=w`62YVlwZE%{;(-ywM5O|fJ5TBz+Qa{Iyv zSF=0z2y-i={Y%C|#>b^XYwrZmw%&kX69|gk#R!pNy@i+O3_A@vRkwXMX)z8*&)R~x zV#d8(RG^~F9^Kl*?mE>`V+3eJryMEX`TQ1`N*MjaJG=Fs8k~6AlX(>%j&`S7k z5ddJH;@FP<{(X6Z1JQlWp$e(iQCbmNEt)9NLEnQ$Bc*aEP4^XSC@gJrGRol7XuadK zgGu&Q`Pi54bFd`?F2deUq;9+R;Bw8*VnHR)`S`BY3nF&e_*v}Yw zN&*0fe8(N#xoqDB5|;kV=_e?_j4`zdB>bx4!*vY5d~L!F>zcSkpW>2p-4A_%oMebg z`u6rN6py-)aJ3EEvvvv735XtgsK@Gmw0)@&O|1=1pxV09);vB)R~nrb8xXppivUK@ zgJNfaolgO9V@AI;+2L`_AGp1jZcjKhkHE+#m3=JTWqdhy?90z#N~rxWZ%#M5`+ft( zGwi*5IfZjPd7MU*UJEV<-B&4AeNL*#Ei`YF%h#v+vQ~$yonce2Gt}K#R8*w)|M+_A zu&BPTeVA_PR2rlk=|(~X1f`Mgl$LG~kQON!LM2p6x*0-2YDnp>AtVQcVFvh}`8?0} z(eHbGuJ_`P;evC{?6dY-_qx~GYp?y-o3(aOP18=lnJniS(7GYD);1Dm&NV(lq*<6m zx0IKcht^BOlcP;m4y5XNb4Ce-F$~UT(D{0ya-mmAQ4xE#?**jM7Eg-`HH^ciEBG$A zS$MPz@K!{yUD2+b&0t?EuI z_obZZ#+N8$6)2=C7u5EO^sbP2vC>+J;hJ83ou!?vf{=lX2cz?zNt+>5( z9ox)f=h}J*4kNVNohW&R%hmY-!~}&z^FD-mmRbdRbDzx|cZzCv zEypY`uwd-{wd(c5M!V%(pg|3R`?Ht`*H7Y?y|P&w3X7wdQ#uIojcZ~isU)pIRf2oo zchvCS7D-{Z<&;PX3aU?_ugFvs&4b1`Bgb9NZgnHl%nI!sN*}5ar(i~CpG{R=HO5fqKAy7JU4`pD^9`Cx6qL`2XB<91qsqw&C2XG+T5<2xVSFFP>YNlFZ@+dV5(duyh* zgPQ0O)MU*9=CK0O#TW)q;(hBc7oboR;73sxL8;y2y9dbZu`KY|}7 zeR^ddCD94CEMra-J-&CM=no#HDSe6Uro}`bKvWhWR9k7LFv7seU2LqU_Yt8&$@Mxe z2Pc&F5OzHOdhl_JA0q2D;Zr0R84Tm%6<5maE8&QXwZ#EB>O=lh3(COFWztyJ<|Fdg z9d*FUD?iX(rB<&~+JYAsVla?fPW5bVjJw-<7K-18`1+w){hL=YZjT5prxR|cg}*=7ccjxaJdf@$7^tAmD*9j-0ziHmk zlMG7#?14Mop@v+PY0kQ$9?|EDO3N}k70UWs=!t~9QG&;a2Do&wEBJ~q=dLcJ-NCaz z{-tWi1;!DIN+S@Brl3Ko23q(9QjNI%dlZ8U?$WaoSGf z?`{-+d2AkkG0uuM7E82+M^9h{h_xE&VqKh`r ztA)NweVZ&yUYf1ed0$A}b>sI#{rso4k{x-wD7?eVb?6|q%>&;rJLR*QcB4|hx$ zanLC0*V@RpcONnft>KYi)X|Z;?a_@+Xq_{-xH^opUqbktv68twyNcX!NGM!5(?&(e zii#Kl6fm@6vI7{7G=CD zkmGvuFh?G%%O5W!51Lh(R>mAbxjfCXMq29fr7B~4KImlCf+fWRn)N;65sr#)sLOdv zC`Ul-N3neV(0Pkk*sjm^mILPHlUR?3MX#4zPagFV7-~B#EtUrZYM-jlIIR#Ws(cl* z-eUN^&|Owq^&x}!LdE@^nP=lzB&UDOKu$L2Eu`%KgE^{$7E7mEy zI=sAu_u3|ON_FO!v{%HHLk-Rw@`E_zjS2BOsW7xFG^fResPS*pZ+I3=CFXIh`w8$q zjH@Sitt3m*gnFe`i6xCuERfIKCtZs=+6`N2FF9ekIJkZK^4IM99y38(Ic00~nOl7& z@&1u5`q4UU_g1WFl_mlwZMe8GO?j~{q-eZBs!PpPY9 z7NcGH9c(y}17eTct2EfoL_1~M#0qC#wX#w&Upo*l@W~zZO@Cm&>tby8D;}LS2Equj^sSB*&p%vmNfc zp*Dv$UWX?eSvG*Dv$#m+yuKVMzZ?n6S4~Xuo4>)q0Gp-eZPD~UpW?l#;#^=1<5c;A zAim7qvhIM%TT^PSL4TEhS{1Jkyct$_wK_-kX@+z;bL%8)V6M08LI#B36*~Im{Yg@Iu=erhS+Kq{* zBgSEhcJ*E*@G5@C_*`_WZK4sGZJQ^E2%R*n$M+gZ9#uQa?p4~n$+F+B z(p{8NThT>#w$+(wL>ghcwTB-yncVtyHXbznT$QFXGqL~=wLyjJ&7a2C5TZ$wT*w-U})k+&@jJByxp_GKDEW7KK9Zq?4g^^#mdV1u=oQn;WQkk6AK@A7qfUq=Dh|+XF zVtk+oQ!LzQlZG)t2``GmMHnSl++{l52b%hmryWBPO-C1pv4TS#-(FtQE;$AF;Yz%G zsnY~$@i4KpEDIa@5KDN?p!>`3KNU6q^QkwD3Z7~WYza|K4gT{Z6wL?J@X+b!{x(24 z2DJvo^U-h)^K8M?AO^u>J`>giFrve9W#c|)RlX8he(qDUh!C_c?>mqKlJ)J2Af?|I%uZtL_3AbhQTMrjveB8W0S)9k zJfglvDlNUR`g9L8a3OT89udVBqCir+Nr%_R-GMnr`k~dgMi)4E??k!8vE~Stpdyk5U%~JYr?gJjO5!d`1_pDqNK-3UyWCV8 zj-QW4CibFg-O*mj-vpHXTB_422;8QdYY(jRWnpfhLH~@Efp=G`bl(G~N(#hmk(Iwn zHuQ~+eAJpFNW$u!*K&w@R$fGh^5tdP>XV%L0jwt!HjX8)B$aNj8}~`tp+0y_Pw69= z^*>?mT}FZV824kC71qe2WGg75BwrW?;_$-)iPR*N`nR2jU+x1N8}R9JxQDk?1Dt89 zeTrB{ToPNwyVoGQm^-lG?nh#DF&V2=IyA4gMRVZGu?I_vflVVMHu?_~XPd&CS)lLE zjS$5HjXR7lgk(iaYKigaPJFvx-|&v;h|(z0B1?b!TM}vkNAxzbOkyQR5F+C8mORh_tMCaKN*30YoPoJ054!`FrXz0k`Y_-;Mk}R9|KLQ(5$Fd?$`G<-CG@f@@-HL(FO!I zaUb3tR;XH1-7u^^h@C(iI7gT`Soq8^A(K1VL$$9O&gOPqC#ttbJ8`WAufX}o1Ke)k zqt~CAiSC3_hQyL+9cRkFivll~efFfdHocmw5>!{nQ#O2YU28{)X4Myf(QC#e^zm!Y z1&^Zr<8S*0zamLLD)*^@qU8DOQY<^Y4PGqsb&rA#ZjK3HrppF=KP_SBQGeZvptdrJ$K~Z>C2!ShNndVKKo5q_@)hv;L|zL@(ED#FFZQ-Fq6}J2}#F0J`BhSrkgE{s}7gGyFGMt z8Z7^uV)+QxhSPbpVrwnM%-==l)a7$|@8~^HuYG-d|Mw(mylRY<*4+@I-|v?gD1c+} zj%$uMK_2f$j)R4JuicA67mw~RoX636&yoOHm>ap>PP46inxv0Hq>!VP@RRV6{iFNQ zO1CmUe|la>9LzfY@+8{43SK^vOno5seR)d3KV{mH(*-sV36t@ItkEV9m<9U zRqws6Q*Efd4M;cR{+ziDk#{~SU42y41HWn{e(+*Bnu1;;Od*)+p8A`~wDDm#Asbqi`8%gToOQ^=P6u2R(faAIJGtZfOMcXBMu@saBM3_$!o z0A(Oy3IqzvJQJRednlf8cw$Nr4NKb0CX*ED7~^sfB0b$33IXpo z?Cv@fEKaes0Uht26fgqZ?wkH$F> z!CT;7@l5qwAab3RNX!hbJZj7HZfQr3`O^tFneC4SrduBjy?mu78YB`yx2i%C!7h=P zmo<>=&A5%dIGp(oEgOUSNu#-gn|+{JQR;sA#7*IQ8j~1(89Z%9YCd(Zt zwn8M^lZhhcND^9Y(;O`O<_v;r_Y@+e|mrBdk|@#arhdtyF6iSnx@a6zVn2r@qXdLQO4v9 z{Y{$%I)0f3@$Z_@HF$UBbcJ(>s7l-Z=SZWVYp)q~`tDT!BWPF~g^dLzSUTXCd74v( z>Q|vewPKclIV-PWg>H?2`?S9?kbTAmohNJz!DJ|Zx;VK-fQbs}+$H4YEp$BcN_Bqm zE#kb@c6XM3Ym$AZg2Hlv#7LaYw4@Jq18$@AEtI34^=Li+2=tZfDSUy@b4^LAYE6@x zNf;)gM;}k{#ADwH*qnToJM+%u*S?oFh2jlYe5>mEJabR!*KnaKf2BZ2&&fTZbC+*4 zi;X|4aiDqvRnY-kMfSI=^6ScoL#Tl75y2ORfMR(257_hI=5S-{eACVSl!m0o^_#QnFLetcS{wu+Ku#vkT3YnEPX3p0&Rg zPM(_`i;OSSUI0oUgr<(!XK_(2MWiF5mZqm9FB6x!&yOmxQmT|cJZ*ah?*#(b0R z$upDApNI1Dn9mo3626m^FWZW@7Y z-CkRBAD$CxVWJwxdas6L(=qyFk_-_v>lMihHfaR=dsWw?LB9{Fv0ww7aCZV z-W{91>{{ir0ZEnVhLMV_`mrT)VERyyuTy;;e58||l;nG-PojiDK=J1VKTeic zJ+y8SiQ|Q#yioQe{|&FvYdfF=c&%r5lrTO8kt1e!L3o+C@R@t)J`z&td|D{C6Zk}^ z4nuIbcBtZ2zM>xOP7;>`utpY;nB+xh1vK+oJoAHslcM@NfuTnN)I zRj(VL_m45khCM8Z6%NE}KQIaMX0Yid@x5?Ld#l>p1C zfOzj0M#u`c@TDB1e9v!#a{K+$O!CE9E~Ot=ONNuo2j|J}zJun5#TMkM^a>*;+XacV zsWtQIZz_%IjN)SB9^2U!v)|`=S*-UJm1@Gw@@;=*nicdn44Fb^*3=U45KjJEKo8j<}nOhReN?m|7_7Cs3g9#_& z;$k%=RRuu3^{z*3?S-xszs=W^8vE`I2=wyN^xO9APN#BULOj$vo;I^k?#Jos;Po*u z)yNeJ*D>tBe-e?;h*sK-re&~`Yl3LC_ne1KK6fb#XXsj}jcQ9QTT8(B#9%fdPY$oV)74Vx=D>^W6{Owt z*7bG7sL?nPs36U1R-Tp>H`3*{yMOhiQ~^? z@V_S;cgHBI2Cq8l;tnu$C{nXVje%`V568xX6jF4R(=fu|ef{#|VwFqQe4n`U;Z7ek z5(nasR9PX*EIh5AvSukNq@>~ZnaE(5V=;SX8^w-cC+|-qB_50&xzjGaQO7Gg5bP~8 z)C2G1yr9DJ+i$0GjlQ~)rm8ln_go9eOS>-k%MJmvdt0N3kg(KTy1laO&FZhSOJKh_ zywEGHXk`BHuy67wD(tHga~wi#-4J3VOu~os%Lh*|-99MjpFR5TdK6>Aq;ZVQ>P}WU z36HDu%AD{Ungk?#^t$z>w58oz$W#GfuRauw_D$3|SEx$S&{wcI6}6<1^ZSx&#H$Sa zPDXXu#Fwr2=w<1v#2Uqe%XA%<2kwMas;W(Y8FY!QQSMc~FV3O1zHY;gWrbPy>Gw>} zY|o*3aHkR#h%Y>T*k0E;I5YQCN1p>6R--`BthK$=UuH#=xc!EaKJ((h8ES>DRKu6-Huu}Ax%TQ~oSaB^i1Day1 zVB34Pd8cR<_-cr|yE#rvsg-53Umk8Kd|^{DMw_%bnZBGp!^D<_WAQ?JxK*_ZhYNN z^-xpH(q!gwqW`M};FH~wpBqmWDyV9-?0L2AcunMb6yzk`&6Gsz86Uh`r@L1Iu6lMJ_VHEv+J+IC^a>mxzzTR1 z1`QO2wqw4@LGuF&+^;1 z#4c}F%U>Zvk7>27W5hv}s(AhKkz~?BK0Q{Gmto?Uff+T8bwHi+QI*~?I$Lti3)j3) zM#A2je$b>~^jV#6$3Ptl_u$mhWMyzP&KOAqQ+yyzusD5q+sQ>0BM3aX=>|4io%w=K#KJNK15s6{xF83aa<8<`*&m zHdBR2%!WC2wSfH`qL%Y*Wt-@Pm;H%~ehZ<)_|iR!j=Daa2(txeb}!O-gvH3*;LSO5w%p> z@o^7lX**5`2Ko+V;e*!$S3B~(3`54%G4JuNmo#L%3NM%{Zw^+<3Q?@#dT%&u7O*B@ zLBlo{76)mRzwmajfmGy6E_fNDJ>kb@5A68zxXM8PkreaxX(@-D@IjgbvJ%teW3JS^ zpIXn(sA2cxkLrJqa6H&O@b~g?^x?|B4>H%g}iH_Iutt zle%h%WxEScJz`5q%e6pcHH|WP$Fa2xv-rTO}S0=Ya#%Q#U z<}W-E+%r<>^2&yt*>{XYF$?5ZqsHANzY_IUp7z;nq1+k)lmhMGTfkK8Fr@Ub*^mgQ zSVxg*tW+;kls1XDmEwth^)!*D-7^B{Ica+DhJu-*}kFN-vP*}u-KAp*i&~2YQ z3gB7`nc=}~n+9B2(LL#xcPq=1rb!MtK?-lWdYtSF+QB+gK^aDGf_aSuB7`+awH@rRr=@T4|i96zkdk1^{W)02+`p6ZZDcD3vP|# zQJYEMXpxD(n+LnaFZH(`Hj-pw8pEY!0v*ZFBsKk9E^Gvh<77JfzZ_PVZxd?c!S}@b zCl}#4ht*C)e;bq)zvl{SD z3!%CUM<$pQ?VZmQBl_-uU)ZdLfQKK53IMg@!=Ka<)YBMxDBtJr^CMYowGphQJ%+%F z;^{~12+Mn0p|!zB!Y^$ud2w;Slf3%1ll3NtlQN{7<>{2*4b(3Q70JnS&b}Sq)M~@SgC=AgY>98~1zhdnbKK5;T?OX{ zqt4BluY#N*fr7s_*ZZF|n)R!1N+-WDJG3-!dT!)t5njK7e{SI)SKEeR=$6>tIn$p# z6Gkwe05teCwJkn>2rlV+&7f0taAcpXtyWGe3Ob4v({^wuFz<*^6W;doeF^khq?x7! zHqMey;HvEX#PuO?s};88PsZsI-*oUgG}86ab^S<$ZXF*s&>Sf@Q7_|hhuDNEw>&iO z^YVbn182n6Zbvv+6*#nqbS*1)l+HUJc46K3*h6NlM{0}SH(#QzL9eS@Q)*?U<0$@| zc_zl3GNgMTjGUH~Yb^#Q_kKGxUj!llqMpo6Wc~nXDmLOImriwK*M4nx*0<}QI?gXK z%}^LT{_LnSpOXN(PoHqofV<(w*=Izsl_Lk7MfD9#`>4aVsEuheC9vu|ZlA$|lN}gU|M*AfiYirx{@vy=4JCiJmMy16&d`TCgc18aLho~{e#fIQ|qO9C=^|C8P1yd zD{W&XZEB@2d0Tj^${KzH+r4yY9R43y^^i{&rVz%P?>Ks8pPLqip%H`-3^R8Xto{EM zJmp}2Sq?C7O-#IQ)ffgNSIFUYg>b(E-%c(xx=If6eDVC1rROgJj}-4xF9>)fdz4M) zqCwk~uKhXEUOo9F06IF-mwBKS=Jcr+kw9^^_wYFdv1j3f5YqZ1%OwFHN6GJny3RPQ z!8I{TTWfk8J??NAQpP_F2I0V!C3!`)F_yaY$>xH%&xInqv}I){a_9Q~#fbbMQJic? zp|eR`+dXTlm6=_V%4umjtU}tbl`?T0eWGe6Ck@jIg?+Z#66{>Jwn$=j(fJ~otP7?C zi{D3+{U{GD?`N;+f}*!m7#Sqcd-P-2`oYIOB-=9r$c2Qu+7^?+cXvyw8Vp9o?dUx) zjkkix)6sU>(nsXUOb#+UIoJpTy#r#D;Ve0`Vr-u`V#&sicF>X%>h7e4s|S}2*b&FK zkc?4lt`i&tC@X)9M!brP)8M)}$JQD_x`!l@b$nYh8>E{kD&+))3nm{`C0@-)T#LB$ z<@zqUom=4+OEV?)FLysbTV4(KAb(P^t>!3TH5jq;sIH zAPoIR&WLanbwwP{@TFumsrrddTR7u@PTNpWOe( z$B=|xVp3$KLYRTsPwsmo3ohrvtLbn$hBP?Bu(qywPV2*W3JOMvgj3Ur?j!s-$AQri zdYR}r(ct~v)euXm2~N{S>c+l4vPg4L4gFCHN#UuhDjmoR3Z3VtEuvEMO~eewq!XMu zmSk0FE|MnY7!~{Bd&pFStI3=LJ@R5(9uSjC$?0QJ*gM6>azUSQJ!s<{?^sx}cV|uN zhA;bF;j~V=r`GI-F(<rdT}g1jyt3v8f=?J< zB6x~`kFD~E9{AQmqp2uxSx$wbq34Z(kw(A5ZQ|gkKUV=43*i!QffP*aVqx!rW???Z zXS@9Rk=PZeyD^~8v6e`9ad)SGzt(5Az1n{!aZhLY%P;wwU3xxiRsx$zUXt+Qf!ra< z>qdlI(_^29UnA|wpAui^d><~+&`CeI=3MFCPSbAyvdFj?ts{dOt0a@5$Q_m1EILV& z1>A|%kW^8P%>L{W_|J=`$t6Nl=PI8_w&=$7;d>|1+r_(&HPB;Wnx7l$D^LR+IAq@3 zRMa535&LbhJ#);CE;{F~SU9XXtyEI~_)>!iN*fu}Uf1tw9dpbhzu~;H#hNL3Cq6aX zQ|w8f?Di-@@zy|gT?00~!*@ctS%Pl{t@*1rFTZBiq!D@8?;k8L;pcthjVpH|=Qx+g z|D?%%UOy2HOH?<r-t367yd$IPb!5C>C7DZLnV#{O9Fpa-6sBcxaDKXL%C{V z#^b|t5*bFA3-rOa7V)pd>O?t5$vKTf_i^ugG`fk1&v9~ycK$x?>)7;Lmg#z%Gbf{t zbqFg~ak$CG zIMPH{aSdrgYEY+-QtAKIY-Vj>{u?v_rS4LGR)iqARb=((p!zhJ83Ci=1u;G1)h_-^ zmlZn+d_!-1$YsK0b;U<4Z(ctLIZ2G1$ns{5g^dRhK(oDRrfbVFzE=af-IzS_QOJJM z8@{%*oL{@P5vW1nw%Le;sxjN+LUmdLaq3qLIK*CCx~Noi(dRnnPqCSdy4lyIxAFPf zM%1|74KkdVzgui`_vW5`RdC~Z!IvGS{i8UZVb~k;d|@Af40&yY)iZ|D7u(L67Y7ZH zFR@Y6a^+fLUgNx0Mdi)d@0y(pbu;eeZGSnQzu124+>y$o8%7|xGovC5L5DPndwj|7 z2J7|TCxpV1*4c}Xo?NsHMKasR9#x2I>3|@iRLn;dnv0X*OniC{?iT zpW^YN>d|xIS38iferArRHbkeUlf(QOwx7Da8QAx_;lIh7=2wS$U%pi-5D$J{rLrdJ zao+bVJC*KsBxHJVxGn6|@JET1rn{`%V|41Hkh%K(&t6bwN39SJcUJ($_jQbl3kz^U4?90H#E;IF;+UT2-^eaDJFzk% zx;WKIze*poCaTzIM{4X~hVWTv1ho7-pOWYH(0W;3?** zY;m?_4>f9WbW}6r6D#n4%mvA9`dX%dO)QM#y^C3I=CWCaSalxD7kY+u+RyAm)gxoS zmZp<{3YU2j&K+kH>m5Ige~R4FGxx!A9l{zux-S7ymqm=H0Ec5WZG2(?I-KZq0jKUn zz{*R=72tTXqawqXQ$HWzGQkg zbMo1jzl152j4{~_H&|%Y=wG>w-YfeH7b1Al$}mKG;Nft4=gW!g)AmG*D%8Ify|~7% zIm7wVC02;^a|X}%?N7kh%(|xk1uDSJg&5|R$f$2AjCbAw9x~F7CshRi6LlFEz){*5 z0I+4_PRBoE#`Tp{TyHcsrDgG9!>FoQ2Ztp#C1lh{slx)mxoy43mKd1HsFrS+%ha>- zSC~9>CwXR5XDM1yEQNZJpJ1QEg%_U#k8;B1e<;kY%O$DM@g%pewdfpTUy$KtcSb;F zC+oX4LiI)`Wkbm|_gxj~h0YA~>;h}ksYC1IHDr%-i>~+JmdS;y9zn_q0Y0?ua6oJa zA9FA@Z|Ap^>_Kx(M5oNNwGd_a<+sfPJ_EuP8A%0tVoifgY#0@!iI|WIV#&ppJ+1d2 zP&RG`cmddR!NT<)Vf_!Q@F@eZ)2FOqSp)S>mXQ^!NWXo%6OxEZ! zuCenH!N<0k;%BW!3)L zMF}Ws@0|>N;9FT&*xG^O-k#wy3)2c~2+T@aPS7FT0p_3w< zkG5n9-)${AfamqUoRvG@U(|( zNF*a)hbeGPsSyfnSO`rbLoP6~m5b|a+%1208b2>>A1d(-3v8;MenQ3NkCN&IbwNuQ zfL0ahrN+ht!xEsQX}wQZDh14r^1$(63I*zuP?>7}xR(4Lc%W|)c965w86x-Hi|N&n zlw3H$j-V`#FvgX}JX1IvN3{rEd~gTEl=xk%)l+g4{fYoONfs$%o<~ALqG@Saos^XH zqp|Y2;~vjd#~-l#grSP^xvDEFri%Y@?TS=fn5e-;t4;BsY|#q4^%gnGUK{LRbe8El z{bT+1()wSeal4((9(3thr1cJbYT$g?LxWMmzXcfN$8+FmTLKmKM%b?{0osrVPOgus zD^u(Yj)cY1I#fG+FRXL1M7ryz+}D=`(?2)ZFDBrl2ZmrN=`LrWO$C{eFCr-B5n}S} zWcqQDt+s|-40aSAE~kQwc3e~MWfEFz+lsFt=k_Js=tyoZmST=uB4HoBFi+!@vCev* zJ$(97AdtxUybh?4Mh*0}db>_0|jwv#g?r;jkxpL9J%0apms)V=|_ft5TJm$+m&kD{Nv z7|%s8>IjRRq%uYn>54cmy7jcxm1WeCrHSE#?(0e__-_FsF_-D^zM(Y6bG@s22l^46 z@6{0l?IXOUJRS3mW~?M4U_WbT{P%SZZ9Ip!J#bxYWgx@|--ur}`Sae}Pk>2+K5^~H z_@gL%W~ljScCQvQ!lwR!VbTpR2TBf^t^-&Gi~atQ%74%WLe-310N)>v#>HGRT0<&| zWU2!!Z|EaK1N9fh8XlmfU`=CEma70T#JtZ?<$kln_w&#X?<_K^0ly6`FS4ixj1cx& zkwa2}l0!d(paNn*N3oT={R(E#*fL>Nb@WiE&?+~2VEgJONWs9WlgiWfu?>T*O-}?o zL;!DFP+u~<6KV(xy;VNb{QcROj{`Xn_RY(1#X45EF$BgH7p9=MPstC`xmi}<)*&4x zfyam3NT-OV_bKmCLsD}Ed%nK!rLu;#%Sh%2HD749$lsn^HYcU1vQwu9o*PPrmqHc6 zM*qizBTrH6_|!?gGWL(KD4T#+q47sJ?V@79uLQo~aKZbhd*zWLmBua4vXGaYkrOVg zf0Pl0o=D1OqzssU91|DAeggTkTB8@Lz{KUw%}J1~`hgo==y8ikOP2W>(kydwAEkrw z7{G}?EiB$4^d|{O8Mf#}vq?Z~6%lh?dA4-q9V0A^R_(E%kQJKYL%y=){cn zI{0R-C{+%4lzzz!JUq`8tW=ws_=0i2La~%tJ&~zM~KRs-y^Jdo_cXyNV=L(4v3&R@5_$(o_DX zm~LzD?Z7tihmnwC;S{V(5*pEV9tT?=UXTJ)W$DGA;3LuoC32KIj{YO^w{0jMVF~~8 zAY_ejOCWk$2heBgc#I`Wc&&;>;C)?ppqwP_z5?9&-wpNf=+Rle|G#oQE+Wt+NE@s2w1F0lWif<9*W2mm#KdQz$%XKs|(Af4wTqY2inn4c=vkQgfp z3pbfM*V^MyFDdRCf$b4oo}T#PJ5UMY=~0}J5km=W%Xxy$$)lxr&uVzO!bTmV72Bd= z&cSxxZ{B<+R@$CvnUVWhg2&*jm7_FO5HqvZ_ruO(XssJ=+?JyD(LsaR%-WqB<0>p~ zes1CWUc}(kh$}Acj-x0i03a6@Wk!Ffo1!x>V6Dzgg;^+`^uMv+_)pFb(Q zJ?ZGxLTmlJ%W)9gvR{oFG4K_a)-1SC1j}cn_e{B_5_*qU%;xOj{O^)68`HiP-6E&e z5J$sptBfy;LO)JE2HHKz713Es5^;%asB%#VpBLPeNu_`k(n>(=kWaUpGZY?C zx`a;hZ!UlIGs50hM-QKZ_)1!$AHbb6T3TI8<12|Fob*N^5sPNrA1_RP5NMs79nX1^ zr9m|dl}A0y%%hdF!*KQIT+*tOpYOCfKEDZ>Y`Z9r1`1ik7Q}xv(FcV(bL;M1%g4`$ z;OtPwkK!BlrZ1(Xzo}}14pIzB;8qx_ny}^?67D9|(ey{dhRaX^U)fcV#~(yB=0Z_qT~pKj&w$kQi=g|K z88!JbmX{r?7hd@&E_4rko@!SQ z7@_yTy(q{s}M^b@W z?9jfKDh5_r_wmeeQR;9AoKjc#u=HZx*ES2FI4c}MxPN58^#sK+ATP(C{b9q3f!u&e z2R4*Vs{xu=YE%ThXPI3?V@brW*X;8d()_0EKYamw<&mEM29>o@F!0Tt&$quEVQtp< zW(;=|ReY98^e}!B8mh#?qIZiwqr8&@@YSQ*5*=S;|8et)4JG;r8~5B(ftky1&5^Jc zsg(#g0F(aFt_S*&A}Bl;ar#DI4va0OXV>1o65ynI64ii|{6le}qF)-LVRS5u3;;i< za+k7fA)S8Uzyuln9ezz#PJ##3nf3kMp`@$8UK618;b|BAGGjDA6f5jFQLuqNOnAQXYB*HxgOZGs6gtMec zuHDa{fd63oH_yFu)2op7|pN0KXQtP>BZxPZi; zk;Kf4yjgOPD^3a9WW`(khC(V6u}L~gAL_euJauouhqIx;YTSs0Ld^!t;h+B7V7wi!x`o#rTJ3n0%4vw((SVo@`-on^9LtCyq!roZi+voR=t`-b_c^ zQKh&nZu@6NiEpRl-?!Z)%h;nzSb z+H>aXzx=9xmBhBbk)}<4-^Xyy6X}+t1Ae6(xV<39v~lRPG7uo_1md0Blmc zV~9$RfgRW=cB=)7YdilJt0efKc)@g=uJZdA{#&CViB9hGLb%p{yv25_X4%{Q{AVu# zRW0RiVEv-_lm|#|tdNU!i$9_^#k=^BgY0>9%kBfO6%5>?UL3gReJ$m{W9}m5wpTP? zvfV#YBD*+RG~e2G|4@2sVH&}jucj?&d+d#8iY&qgMf=8z2$g;bx>8#@OYW?Ct>=C= z+CJ{@U084xbVQusw5m5XyAbhfuiJen*Jfe<>-vqytgDa5rSeW8VhLIlz03dl89IfD zpDfU`%mY)td)f_A`@7?Vn+5+{9^Kyrl8<;8Acc4D4!cQ6=PmWFQylqI7*FMr(C(1i zeuNTrMV>)k=95awcs;>zqR0_MvK5bbVx&Bc;p{heZ4QbX88D{Z7Ik$U3w%A0bStep z=vY`he`w*b+xYpG+I~tDN!D|{>h#+HM9d9(p!Y)_QQOWMYLRVHW@bI2;WP0~)S4-7 zx5KzuY61BY`5C&^Ev+ITm%71PNLV0F;H~;KPdodytwrlwhYC$F*Ln0Ad}K5CxlPP@ za^!&QWE}}Xd@1x@DjIyY89sb;+TtO9er_q#M%F`U@OY$bmhwm0N}a&>v5PVWV&UJy z^RoTMDb+8MM!s9CD)Wx6*4w%O&}~}w&W7l`&&yVZlONz1{b=(4FHW(<{tXdXS-#NF4iOO%)`i9w{Jz2u6#l>=kTxPKc9v=PZcvPL@q_-e6$ikiH*J76hy@z~1c#u*A7~5Am>%j4B^qFG zGFs+AIvtwEDE7r5EyrCW89W}ubI0dXZ?qMtbjc{|A7P(O2ER-EC8ml=nn?sM$S{b zuzVt@sLn&^y~LQf75SRN;>?xVQhDLQF|CtcI9%z6D9h&DXxjEccTU|soyPB?av`GJ zG6r5{pgcN6KFI))`}%~5Ae~R?>sz&!>7}i7%>f3GPTCkr$~IdBT`KYo#qdQRNMA90 zkKA#hr}THY=*YPkO}LuuN9j%=0DFnC6&1{A;UeeJf13xTBWLN~lc zo+cq((E02>y%6?K_T5L`r!oQ?PLJDuDqWfNd`q?oF!~na8D@_SYVCR@*7(bggik5a z@Yycl7e8-L^!ybIprddn!p6n$58?xe{=VxwN$8_KL4WPfI(0qpd4B+6i;^gJfD@VR z&vMH!e{xMN3urP0%l^A^Tyc^IZY2M!a!^8a)VHGRr}RBL7nt95K7G)VyRt1GxZw2i zQUxdqF}kZ);hVM)nnMP;u*@~zjpk}+o(RZ`;hYhhBmJBg&55b>d01`x&S1vr#T)HI zRgGc(x*GzhV0@U#b|#Qz8op;-UPkb?1tT^JrdKIhh`vMiIqZ?B3Ukg=_57u%LdYBK zZ>(a98Bq2JD?Qe3$*SZH4*Ko-+vxO3;JqYvnihB^4Gt#-Zc~>U&n6BU3g-R5JvOA`I4$ zEli?kDc`FVQOhX1NJ2y3J-c5T`}NKkQ|*4sZ_jXD)MnnY{JdWK8X|MU(GRg(Pb#k= z9=@>12XY)5e0QTUQ@-jO89csE@5Kf3V4!H8Y=9phe?2=tAN=tHwG7yBDN~-M`**ZL z2XI--w)j8D@Cl<1l{>7fu31(BE_|(y$~iZRn_OL1zb-pACI1^)28|;R6%cv}?KeS1 zcY(F|e@TWt04bPbaViL<>!hM0Zt~()G+>c7!}~|sGs3<3Zyfe7s0*g}Wmnm3r>X`* zxmwod)R*eq%N*p$P4fyi_iKs$hZbpp8UFOdv^3K|60CL4u)KtTj}#loxI5Ob6GiIi z4vOk7j(UYSD5tJ(>iCb%TKOV`l!8Eq9`n?jJN>rJ9IscrYf4u?{G$Rn zPi?8&)8OcXBr|K#re+(VeWN268W(7vmrF2c749z^p(Yv!6e_8()dIqT2K!0$wXH;u z7*@6DR?)A9?0@_w&7NRQl$hKvJRD1^n*X4q(t5(5;c4(KQlY_}yokF>l$*j+9%>b% zUB*=RjJ{;pTE!6v47lcGGkQC*C5KJ`vqPms-&SKt^}=Z{`s8r6h_6cT*q-(_OEdN# z#oOpSQixpDxoi5yv(x8?-Awi3?C!mX9sUj+jVm2_L?EW#=9IcGwaP9oF2g#pz>31? zh157II{Dv(UC{%|9H67bmCNpB*-@sN>OX<~KU^5Kus~UZN@aO%IE|w$dzIkqW@P2Z z&Lo)ltw(c%fpqw}%D)8(|CcbjknulLX7oKXrXiKv@nv4@v?>*sR&RZTGmF z`^Tv9s2;hWkA6~tV{St5F)piU z?q%MfO6R-Iv&C6@45w%4MG)1PdXr=DF0k@!`J=Naa3mEK5vCpn8KHbt62@FF{BvJq zTqXyB?ebZST7I_`bBN%eTyheg;*~Jkd3+9cFPlipN`L=xF0NW^d3kKv1M&Gfaz#1T zw7XVJ3U(NtovTE!yPz6$Pq?IdAn{{2Hx(N)3`C5+SD;_AzWz*9zemp;-?N{uNKSk( zE>^jJte{;HPc#{5yi=Hd$qhO%&w0sfClDJA)63KpW6giPRbf|E`Nqc0ahN?^QI6`O zU{QRxLpHQZJRDYs+%LF>4S9{r(NHBt3=4A&^;Ra($_O9+KepaFuBx`{8fDW+gVK$F zba#hzBPA^jo9<2tB}9-er9`^B6_D6;cZ0+RHnqvKe7^5}@B5td{7wBf>sr^GbBr;^ z02SR+7JMYl5wbn+C%S%fTB4gf-|>{8Bq8W+r2?5v*6b48QkE)C#>_S-IqTnFK;10y zK<|0C!c3>!UUfdFj-6po&u` z_9d|I<=W?|cIZg6(}3;M4N-(_r~#i1MI{N>TDzbo0RTrwyRj+y#PxCazB+;%h}ry6 z9Wa1|A%?Uzl2K8mc6OSoBFS4V%~nX)&hq`cTs&|1dzidnb{qfc#Q%dS<*PnV$AGxN z7w3u|J@QzS7ajP4FPeV^I@`Rbx;TkTD;m;XcJ@iQTy=dI&-DKX577apcH$6@q&ZW} zLd{{pE{4mzN6-K1aMcx%Q{7h((*V@8Tiad+I17W)K!bw`7<(q-Z;u-yO?>K@Z@hV!wo(Ar|e z4Q7%mzWQ@5Rnp(jC990NnFcA69)n|Xv6aAf^NYkWg~nR?w(5rUgV|rnLNCS2K=3yc zKM>NXKlHAW_Q$rY0J)skJ2HaP@OD*8$6gbRj}->6WJBE^W1X!lbKEP@ z>H!xJ5j&o=N0JM&>tLm>w7MLO9gkH53?{bwl{|$SV-xGM|mSuJsyGCKg8-b}y zPwMLiy)l=@m|N0lNnc)kVRYy)%*xz#HCyuJINs(?b2NPHn{RCIO%sPwqhya=vw?zE zC6lL;;Ilyj&??tQ@Q*UTvj63mrMUlsl-x}Tss9qK47~=P^KcPE0~PQP@Z`cep@9lQ zb6DsGQ`}8Uhcd+;B{wR(3ve4EJ z5F>W8c8=!p%1*yBELMXj3w*cfrX-npg&Rn(b^X(MCZ8ro(k5TRYKnOsO}?2nV?7#f zD?&z}wBZ?}awRU4(BKkHK662nhlF^&>L$PkdSHpW11@WMOuO74QfJJcaIT%=Q7)v5yLxmN#A$3F+=aju`#~mFhrcq~r zRquT3J)bea_fZZf{8w@LcyrWeaKcrhxVR^2M0ScLJlyv7mFCEYOtft9>CqnK34Zl=1r#h;Vch$APr zN4motPv=T%pJ>VAGtn(jA0w4OycA%MDGtT4s)wE)Rd@2139xiyTS7;l7uB8g zP($pJZ>+e;&(>$vcu`^Ocpc@>@bOm|53K%n^m(Q&u8!YaAA6kwDq2`Hah^SY2FbJf z1$rYhXh%v79IlrUOd5($XO=vH2Lv<^!vv@)W<9S8w%q39w_+%qj-yC|`%igIb?CQ* z4%66wwWux%mVa)%9JVmSqPz9%!paeigjHXdiqIjgFukN`quaRQ%0~QEM2AejP?y** z`+|q+M~W|Cp%jul-BkX$|FvV0*QK}pTmLVSjzmh$Kd?9S$l3EbU+g09TB|>4Rfawu z0g9W`l#vih;30KjyJuM3Qhs&g=;Phr=zjy5LXXdkeIW%IQYNQeRqcZ(g|xolgJB&o zf^Mfm;Wiw5!sD>MbH8PYDwWc)`T6u5P5=xyuZSKY(h`Q1zP$84U99vP_blT%vhne? z7jqhD%4M(?8E`qrUlb+K{ssk zFYRFb4pSYp7m%eWzAKDh`#ThV58fh@Ui+zWhr!E&;>*t5iNNhjU z)g+n3PtYoWllA>gY0oWK3;a?txq#WW*4(=<=<}n%8eG~H#!t_>a5#dhU#B9_q7|~- z*1v82*Z^m^Sbt#hdsRI?_htg|Ay0IEMcj!w>5-%;m(2IQH~yxrKTpK6;MBuCyj2W6 zKuAau;=FJ7p-Wo?vJ8@4S zIW{+jadX>k0PYgE&h}tN{R~t$4E`5-gugL^R{Z5nSTr?-)<|*r&=8D=PGgLPJ(N+^ zsnTjxh^@;HKM&f;y(IYZ3_Lx974YhwV zv1{KAza^y~dx=+yJ;DFVOXe?NK#!$Lx$%R- z29v@j9hq@g(b@UWuJZ3ZXH%Qq7-ogq65A0G*^UPfEVP%q-!xi-59+ovF4Ue@N5$$| zIH-m4t~W06g=jVcD-hL(SlptxiviWaF0~wi(-5=&;yTZ-dI>MA^GZw}4wPfth5Zzl zw}K^JL94j+M9G9iemnlgb=tirgw6Fkrbehcrd08^vnTW)W9} zElly^fRf;#g&?`I6Q7hUUI|QxwWsb}uUf9>-$=Puwxx~IR#)6AL&UE?RQrp6@-1;H z>Pv<~7qIT~oQIb^o6I#waRfrbwOF&ak}Vs>*mKC9f1UXMSKV?+`wue@j4RuJF^lai zP1gS%XTBpcLlmmv?FH|D z2m6Q87|w@S|0|WrhAgxu%?3t7lOPOkL=ynhIcQQ$$h3g+$j@w7Q!+>)KA`;`PFkLn z2-6XEEaIF{hUT#~g@Kodb4K1Ky;yG{eCW4(X!TRWU+hG2n|&0u3107YI7PA~yUaTa zsmUo{k2?gv!)?Pz)~aZi)Df^HYoIvFYmO8(!>^dW%igIt6$VvT%khK|=X~(m-P?sy ztly{9Hctkm$cbdTe47dIvT9>m3jR~#&eBaoJ~&{%DTSIEfluNWa>x~G5|O!tsu~#X z-#pqQka!lU*Z5&PB>^w4F6y^y=Wosf=^g>=Z+-9y{3=WT<01F+m!GsvOq_-?e&{-M z=>vL51uynFPoQ*GJ^a1Mlnujrd*Idd<+cp&S3DA2G+P}VlO0b^2a9*bs#rLRzMgUp zEM-NFt*DKZl!z1kNI#K50~T4Tsz1ez5R0pFD1qU;o%B&<ck+v%5rTM!>0{u7pY_mOt8le| zDErmRR0=V>a=*3q;q5JHkBp7`HzhW~F}ylPMSXC{?wDTQ#aaDxKu%%d z={YU;zN)m883P<{^bCuucOr9-MuXQm z^@w%bws=kYrZH zCm;`LAYF-OwOr*~TwWo?z~e&?gyfg&qBtK16Q}&n%13(lKfU=ESn>=!S|Foj7l@}1 zW4&2Rr!Wq=ZsFHPZ9T8q!PLvkT7Rtuw$ifCQP1Wx^gQdFZC2~J-sJ=h*2=B>esHn2 zoO{Q%W%y@HUm%Dkf&X-(2L0$L{kl`7GJ-WbFaC3;-akc7`LBjtrQ%n8`#(dX=7+y5 z(uay-Yd};KX8dCRuIfowo+^NEeCO+}zv1mj-G6;2|A#sUq%-;DzmiL%VFnE_v#@3?NTc%`Kn@p62AHF8Oa^kOBa`u zVM%N*h=^NXg+5|mkY9fD-SpGDCbumeTuIe;CkMLhl*)?H%9BgXpl5|)V$7Fkox`3^ zjO;wu(IdE&g47;coFEp-dCQaco$vgU!oVaM6G-0z4GnD|y`m!@W$@#8KQPL9x=I9u zjSlxbdT;SUtzp%gfOs5wiz6k33t`&VeXnT4LH4dz@!6iOv;9MT%HIJt<*mx_58!`w z@a#Djq-0c{i^>%T3xQP3P5U;HBDF>bs(dlRTpIPD{SnH#&ix^&5WCr_LJs$X@*?uR z(<=c+*)#6aw~H*gIqZ|Ig>N&7bQP~#1Ph5nJ5{mPMG&44&ifExebowf$t1A(OqN-# zB<`2VkdKTAHopc~t@un?&xX89q021T!%Zr~nUs^&R)muRTfL~pZzJ+TwLA*VmY;re-nWlIJzHEWYv{;a#kKQn1SknQ7IhjGHC}pg)qpjrt zW{P;vCG_n{qO%+@)HQn-QgywXb#MtO)Ma#f24XB8`&<`v(oRcTHoR;c>yhyrmS%1U z-v|Vhne62^_bwENsN&*5lS_i37E_S3kj3swOPGf;_SO}NE;Vezd|_3n7g7-{K( zFlKi9>Zj7tXA=@V(}UT7b~tc)%5m5bsI3Z**AW2>FK6wVEf(?DTIxhEw@@Zzzdba^cMqCfB--r=YMN2|A1y#{gDy0PG@byG zm|oszz*(j(v{p`^*?EvtIT{@uT~t-2FE9Tez-Q;4#PMJ2Pv&8-xXyo86++wAMh%vJ zjAj*W1+1CTDBJP>13v;QP~yN4mM8A?A9?0036$H%D;MkS2{Tj{gYS)cslZ$J-ylZd zN6Pbh;p1}u*4>)ZcEgD|j8mpc#?rA*y}Z08S64YGDJfB(Z5H3^Kyy==i}gF{LEwvVp0}hgZiZx*B8c%mcips-Sng%#rhH~05_VB&{8|2R&n(e z)%L>{{8lT0jH?=X_4CE=Tag=9)S!3s?GAKV{sw^C=#X7bbKb;z8r&1h&=}>=72O#k z0YU3(96WTcc{@YXdqoj8E^-Tua3)GxlGpdHiA$P?D+67I(?vTrHiQ_%Xobyd1ry&} zIG-3vdSVR9J-)34thWx&F|UuYQkr74Vr0JMMJ)K}oEZuJ%+1ZD`bJysO2(5DO|Iz8 z+SFKTPsBU{*+MGsel$GSuR3p`a}c|6WXT*JW8=6HxU>g8w+g1{0KxhY#{l4ONnMLwh`pqoFUL$nO*t-r9f`ZIt(aAc^S*0rV@BV z`=?w2Y-?jz0=~d77e8m4jgw~n9rf^`)9bvmVyTkk3<++8xbj$leMPH)RAEp}Y0X4XZ4UU@I}0X6>C64O+7D+xY1EMu zVg#I)u)V8bq6=@*!Z9Thj5H5{68@=TQNq)?DU-ZV3C!_46-r3euVVD8jcp6ZpI34H z{X6q#K07NuW9fd)C=SUU^h;k=OO(4CkEp>a5GB?!7OR!anc6_OcIj7MsWl3rAda`8 z#jkvc0LaZ)d$bzBR`DTwWa%DK5!dqbpEq}1U4w_-=IxC#>`?xx@M*cQ3A|pltYf$Q zx)Inu80i{3rl^RM`*FAoYW!&+FGLn6r~Tyw9=OA`A}+eD=BLA4<59JP)A%^@5fLtA zV3SqigEC>%5yR*UcW8XsHVoG(S&j8=Q7+WBpo%;-SzVevo0`8igI`I=W=F(g+^ch8 zHh|;2q=V~&uoTu|{nbN(>!b00<@NuH3*Usn{|={c7bNzue~6}(uD{yOwVUGUkX_y# z&dAxnycAI+;sX#FYJHRTm(}Z^RsI)OsRYP&E~|deLvq4~mjB%l072FDxxMhB<@XIo z0NTXLl?G%DX6eDS92n8fIo1Q~=DaD$p$be^a}d*o2jx6tF+sW&6H9F)9oCtddE-A< zCjo2?5$w&}o`_khS#KGw;6EH%pbx{_w^_s%C9en=k^U6PneFY~BqeE)f;9QX#H?r# zL@4dPqh&`1l_5_?M=Nw2#gmbQW~m>Zu6$wD-`<}nKrLUZwZ_bTzAmw=*h_yOKgacy zn@c4e^4R{HDb8`T+2#2ub`C{-V*`loyFivOsgk|<=?AvA(;SX-l|9!Dj04=@8wz;| z6O*)^&G)cSGIY@UuL@tLr}chsCj6=e+n0p-7B6ER4t^^YQ7VR>8B(0im+F*k10n3L z&6=Fp*x0qcXYW$nw0`kAlkxn<$Fy{RT(^|LBMWKW6rnS$D zHCpz)*Sb<#tQZti&iO0K^C{k#5*r6#!sy@umVy{Cb#m(QaN)rphRKkFe601j_?+ak z8q8`@1^kT^E#-`i_tD~s(z6K`mHo~bQWJL}U(#xm8Uu3io^xlrD00a9lUy65WaEa*r^o%_kMAFM6x zr`m<7D8u*goKBld2g91cjIc3wyxd)>;X3An7zbBZyzcWho7@`Gq<+0}gQQLL;moSp zq}ewx=>R0#3dW856#2_+f#$KvjVIN-wlSU+(Un{u58fs5oGngKg-xIO8T#xcn$Jgg zyJqq6@sl4E_ldf_s~MD}x`rdBPX=6s1oKBFg@nxO9UYtn{4(}CJU?uV4$seerp&I* zaq34!6_(cg9l@FRxtOLz(6HNMq$H{=$rBP1exr<(Y}I^U_L@6(Uujqh0+85>`^BX@#=D%Z93(y4 zMXQ#|-#~+pe#xKszF4Z)nU_A?#K$cf1=4YyyEYcBl6;jBkl0egIGFi{_97QsNHW1^ zC1dq_`;B*3d;8P3_D1SMZ}C2YN@p(fU@XV7YQ&Au=TN!6oj&zg%N0I>p`;g3CHV)ANOkD0| z#8@+<|G9OZcOg#o!m|XF2Cko4ZdkuE{G&hzpoPJ|(83_O;>W-1Y$&?sNi z5qC(#BW{Yuy6|3AF#a2A2I`|mSguQ;Nxx|zw7kKx!FWO2#KYRvjt%tD;Om(v2I)z)nr9Pe^ zYCmOo1jJ~&Zq5whGxU2vRtc;7<46ps&(ZOa4g39+T;jFBsYmiF3!inW$z`In%PfcO zk6lfMRK9jEzA4e>#5$iiK?En@gPy#%rmFq{cdl2;J}ymRP(kPUP!To(d-P);A0MR2 zecpU5!7PqrW0r@9`TN_UVN~O0=xTuVB^iR67y)?*`@A?G9~M`azIv`0Ga4=xSAMXp zEImT58&VJ-5*@9ttS!Qbqq(^hhgm0E84&MFDQL_&swUO;X{@`OMgsc9avGahf;_8kf z#qM1@OOXVT30u3NajMi|^pUw#jZ>e(;20BZhLaYz@ufLo-@%52_#8Qkw9Y&>blXGs z)qV@bi5s2c2uyw~Tq3)sFZR*ybFG!dlBq&da00Xx>Xd|QBE%ZjU@%pD>5n1PQv@7; z*U`)dE2;pcwX#{5OgCH}W&RZx>dWBUl%I)I$+-mzg@^O__Mzen%IU=3KZT^UJK+Nu zZ^+-|o_sOaz=H^Svr*C-YIfD)yFQ29vR7y~l^Q~*R~U#BpsB4tv`Q;b75OEi;`+fC zI!Lk%!7vIm!&hc9$jKdw+o4q~GTc&?)n0hw{1RHU^jh=WFW4tL&&li!TVx&d);M%2 zm(GHCE9<w=!!0lj?fLo6a~ZW?)x zhOl#ed1+BOi`(5blkLP_F&QpQbax{3XRrmdr=2}@wwgUwe-3u)7biErHY&mJB;txW z_cS>XI4CoR&20#qwm0`}t3;cD6+$HQ=>;H|2Z|L`9=s6Aebofm`-SYZu?ut}v}}zM ze`j}a4xgN5I#S=+N(L6d zbwL9Ko*T$nc(Y8O-}K^7zlg;#{Y+l<=aa@tlZo@#y=>f3jaTX}^2Dp!em;DY=#7PngjT#)}4-}&{|`Qq1-z+0YwShQEve;Lrc#_3Gp z+y26^Pl-<)eTiZ`9x}zv@ZO=8i}R%44HamF-=f)lbtHu9_Vz4`f=SXB-4HM!h@sBygY zf>_NiHdCVHv8NND0sN|7tHfhLm%G##wmlM2eSg4fyM1vmb$(Lnqm}_}-ogP4)6Jjv zCqr{(-i%E3bAzOzKoMO4%dB)gIn`xc-yT*XA$R`lrcRJtN7a?TwJf}&S4bGL$W}-I z46TH^tcQup33ciHE%T;nyC4vCq;Jy*^-Rwv(G)YA<}uC(?G*P zR) ztKvM~C*^V+CMj>{0g+FpdH_undeN6^-7@nuIdjbT-rdZ659&o0ughwEGOP6=0HU_A zKPXS$h4_~OOY9#QEZrY30LpaN3E?}p$E@D_BCNY>-yyxDNbx#I>mDjbWPe|y_6+y|GM`SG4R$ljuk znKF)9Gq<{X(x(7}=`OBw?&c_iUEP#k;Q4PR>pe(Lt=FMIi^CS9<=&)o5#?aZus|rB zT(Dd)gs>2LaKH_Ws(%|rdC`6=hYeSKY;_i6oit(&tu=B|w3QFG78 z;XI_OvsLBlXu)wej!p1zSj0GB)Eme;;LZNJAn`QBS~c{DgqEL|BN&$-4@WPLlC7O7 z(!oCE+yfW{PuxuZkkEk+NUh%POZuo@3y_hNZ*nijiq8V%@4^B7b6QF-5+JrE_x13 zP&(U>ud=M~W=Fjt_I&ESa6R(F$i$%V1oGAm*D!ztksA2+8Y9hKeZW}g#CYWkutlo_ z9X$(xemx7sVVq5OA5r7XLO~%IixZUclWnKej<2I@Ui^6nes0N^<2>yTB~G9WOcg#6 zRUi-4pU`?W$KdDbW0s0Z%#rRxFjvz|^mFYX2F9~08hf!y#<1o$lI*A-7&0=`a~5YA zYQ*GzopWNcu^JYq4XqS}dbgLn`~LJ5Mt?p<4BCT2Q!R<33UL9K4D92N{;-51c9HQV zdbf7lvKrK;Kzs4kpo($P$Oed3%Q9gSYo8V=Icds`X47)DTT8@qOQ4WiR>&8ueV`Z?XywNzmdW-aJ_2WAWaLE6$L+s`0bBRS8}ps zn<)nIbN=goH${=p4V1a<{JUsf-YcRqaNTLv{(kQBKTAo#9wFVc5%j&M(U8x#i9zLZ z4h6sd(`=jjquf62J$>4Urp93;ydq&+f@@dTXDa_gApit84JWXJ2v;Kw*nms<{g*xK zT>?mx);bp5rW2cnGqzyX8uMpjeuz93miGuU$lw!mkJ8I;>J09JN$Kq|UaLVRT>%6T zpRSDi0ohQP?D@~WzVnL$diOK1aVsb7tqwaK?+neKLJVhNoa)VI0-5r7zdKEc8k(dC zdzH1o-IAPMqY>vVYbH2CT`RqC#{Asq(i^%P?$2~#KDp62)I2nU(IH#1jM@yt5XCs4 zD@HXAkxQVZbKvbx#|=J>u+e#vmaAsss6`ncr2}ILXlZn`w@^0_WLazENzk{Mg&_G+ zC+U=^|NiiTbsNYy7Fbv1Tn}gI<~?>gp7%BWxF7e0CoX;v`CthYT4mTl2dbnN@kEBV zGb{(Ij3SzcND-c*)!K^(!kQqAjty zv!?S;)a}Mbf$U?1H{g0{8IF}#V+iZ-gSQuUU+2CoTw7j#X*-sS^2&3w#2gR%1pgS(QP}GI+6=5P@*?Ie9Y1E8!Ak8$!N3CeGWm zj{-yA>xenMrKbo{^=i4Y;yqhE4+spKg0VgA`MUA*F!dX`L`sxYgx2JqH{dU_e3fOy6p}||W_$=(OF03OX}H!)SP(jN zt3f}^eSVq?%2`NGPjE!gGl}rhfyGg6^$J3JMMTnuHj9QWG%g2sR90&3tP4EL?LNic zs_F!))>6C z*wfvV`DvZj(+P98&d1c!Qh14)H9D{Hl`K}DJ!$Alt@fr>LlL&v)(z+E-BUBIcQj~0 zt*+HmZZTw$28=rib1rvpadM=+e69yILT+{7msNS6am6k`Eqf;c76G_OY?1UVQhkx_ zbCrhJp%uKKZZpIYHKR2l-Uv1ItQ-OUua6!GYgBiDpkZ@cX+%&W6h7EMBp>J2Ib{#F zK|BLvi^$P$t2Rq~2p+t}^R_ibCKIm8r)OKdjqFFYSo3&Pyvz0$%t67cK0L7Cg9lqe z&u;-|S)^1C94XPD&O^BLu9r2IpI9Dx1&UmlFO$`w;$fm z#vjUR1FjtrHU$S48yrUh6JQm%LHm8@t7vPshY7+x^texm2oD~Lb+bYwq7iV?oWhmB zVfKE~?x8Qk6MDMm)ROl}?Wdb6sQoSj#<8#x{Q5m@q1Tp=f-9Z{#$Ge>I>Zo-{rouf zDL|tG9i|<@JXnucUltNHZUo!E!^^^zl1#ImeVw@%$V^FrEkak9SFJ%-;_oW9mrIPq z_U&>$Z~x}#J+WD;xJw87^{P+W@@;OGTARpE3F~TUvjX0ZpgY`xr)&X_Exn7DGJ(op zVpUU&{HI;;U#!KH6+(OMk)2*tI9KZL9^+y&mIJ5MirF8wNcX}lNd_n{9602oJhk=T z2ls#vz~RS=1H@wAJZm>cnM+^f35M6`nF4SP1%LDlwC)c zvi}Zaj3oGa1Z&IF4vcm6d{s9P%Fj35M{8a3#++hrapHBm=$QEt^)KFj+fKqO!kprC zeyVmKNVYipKq9r^p#)`p*eMl#VaVe$N}YT?iNL&a`LBrurZwz#w1FESAe6p}L0DA# zw>*smE`=Fo_E-bcwJ~x8?6{S%$_FqBxRgW}dQ#xbP9t7BNg_;(Dhhe&yt;Sns@>wv zzxsMhOW0%6B=#M9CF!B3QRT@eQK&J7tri_;yQg777v=yOQUQ+$->WV9j#^90w!jOB z8Y-mAUpJl)@MAZ+AKd7xJ^Z3?h0th2VtHF!R{5FSxg7+-CL6uU;>^wqG{bWT3^aB{ zSSf3o$42R)6|r}cW5wdnlJo%J==*oS|DACFEg8!{q}Rm{wXDLeyC>rhGm||3h*p7$ z_vYCM@?O)!%9_&g1MYL(j0Ud%3^?0Aq@JE`^LXxId1!6M8~5fS_ALJtQ%#g-`BDO| z!NX=DMt>yFViA`cn(NR3gNPPnM3$4c)`lYQVRg?{lYze^rT6UlbI3K_bDMAGLg^kl zq8n7sGCLdJVCmSGp`6Xa6JUY?n{zTb?#<${-{c*)l_ z0@QRM4yK!-7WG~RZ!-jr#kWF(paJ*~?jM;Y1<`n0x6Zbh5Qeh3v>K=aPeF3oKR)l} zh4iGrbK9Vwb<6Y_=pGgXdLBdWqw%3@z8<*2xx7g&dKJdZP0Q}d3$I4ve=XOWef+K` z`)Xikc%Ei?#pzpc1>&3F6|Xr(dV$v3=4kw{e?|qb=-#cPY;lRnrwugc6qjD1bBtU}Dw6K5LypwN_^YNE6wjK9Ce! z*i4pnUlcZ@xZrr_#iTj{uLAaS+5Nqv1zsR8`u0zmS}Pz+Ad5y82=DMZUDqPALE^n5 zqW{S#u-XfcKp{CAERNAU5D;=9#Mqh(q$FQ6 zdM34Qh`C@g`#JWwZ8lp&4Wxzg4nZtuH4}N~7 z4Eu~@kDT&?ic$t1^-=_s9)i_<@vDw!c?ktgyMCw4if@`_V;8&DbD*Tq>p{FaEv-A$ zDazjJt2XAIkyuDiADr=rOYJen-gWmbFn*P6@1&UtK-z%r$`lDQVNddUqgErph*d)jpq+c7fdS=85jRhW1{2-*+++_F&qVn#(qUM7ki~N4uVH007R<`~r zPAiI3E7;C=P>5>FqsI?5C$?ix9NHsROJvWH={MPfgckq19oer|A~IQc%@F}zNeXD$ z8gUe?WQ&J}s{pr}Z$T-PP%Qw1i=FpFA1( zX7w?(|NetJy_B2;(QMC>mS4bHupdT(FuMP$`*20=R@9*zbhIBMG{DCF{b!ir&(R$} zcCl>SXzbPHB?l>h2!QX$h+W^S`&o`jv^<5iaj&)iLg^HQ@nQHu9^>kwg(#rdgLPwF zFBQhuT$(pT7Fwc3U-A+G)^9A1vV!O-!-A&n*==0x28b$>xZQCYLGj}Q(smmJVg zATCkQYw%FKFq66R1W$bMTOInss`?liE}UoD6kOq_u5RfpiHOD=`NxQRwwBtgr*6Ot z1@b}=Y{04J`aDhSbA>%<#SQvW4(K>V+=m;go-a8DN~{NZqud~a;@Dkp@RK`NW5O;l zn6s`k#$q?8{oQ(K&bEoN%BND8@+RnX8W$31rdIOICFa)AzbKp;>kbX+db!de+wz?D zge$}_CXl+ojons`2b21Cg*%{~0VBL_8=J4ohqJj8bZ;cKYrqTch9CI?i6?qS5XkbN zNL0q;4o(}HGjzNHWmYh z&f-l6!+$HKUn!y3gu{p*pilHOt|S%zhx6IVmwH-z;3R!q7z@`$dE$JxFkFSKQuf!) z;{Ptn{YPT0oquh#_IYA24>KkD|L-`MZ|>T;fOWLgo!*$1ME+a>fAf2Y`S?dE31VCO;pC!?$6aI+iIEauCmsL*)xP3cHNxsfU zYZbM+rlSd0lc3-gB8%W@XDsTM_3{XQYP#IlQB2#h3J80W!LK1XR7oc)mbHPe=>^X8 zh8H}Lt803Q$o=&zD-{ZdyxfoZhqq^Pxcw}ztFwEHO-tM9%x)g|&%4p^P|BWNzW&ZM zC2vDt;^oj?i*2bRULcfJRT;VUoy!yZ`-@}vD$-X}tf3=5K>caUAkpN6fBlUgPU+S8jeUwqWha{XY zF5UlPUWxGQm{qgmU^}dRL0-z0oGD$X@I+qb5`APuq;8Oevw3Pmd3% zzN854Qd|$Wlyjsv>lwDW$$16YtQ*fmX~d+T%cmGe$B$&;^@L_6%>nrUoWZoXlJ{J6 zo~HCmjZVqp2o9@$dz911Tjyc8V#td7UVeASP52ESr67B)enxv&9&GS#5=$~#$tgfG<21N3G`Rg?V>~r{pmJg5hV9bKz z;_J))&&htTN8=*2wlEndgS*~xcR9D+i#eu3nLkBDl#1};sNGk$A#MvCX)+rKQzM9q zcuC^n9=NvT{v7YDb?M|&yw4i_R$8m|Jz{sMt0Y81?o$vN{t50-_d2sskqE1sl+M^@t3PO>0;wq=KO-) zEb%<);{dMm(|a9l%o5!V&G%TpKx%>&$vc99Zx;oZaqn6PQMJhD<;Vv_4mede0uw=v zR}@$SVZm~l?^nkV_lw!@JFh#&M)1o=g`K;-MA){t(S;t9qC=Rgy48yxI>3e{ph^2V zm&gj`rSv@5P1n=)6O!)}-OhQ2lQjl zkp_&cJkpA+Qwj@yh&4Jop$LGurVdbtEDMddp0pmC*U@W>O}eX}0Vn&BQY2e&%Gd)r^{hR*{%lp>XH`Nk{2v$YSKro z!Xn9k$_+S?kF>s6DpZJ!WDnZ>iGk$GBf~R1KT$I%rxbkBvbX+fYIpAL|v+Z%2386^k;f(ChANEl{ev0)CS?>-W>Ej?{vK!XtNWorv^#GK{%3d3jGa$y_QrBFJM`t#qqKjB)&F6HRG1X z9Pm-1Y?qF=t|vr*H3GNaRSWTYw{w-)>3WCz9c7b2f9_NX-d;d}Aci#R9p_&3SjWlWu-hDskbiejLM{Q^hj)0qCe#W{oXV{l;HF zaubG)0=^k6k@nSzMG1?{Vj#dWFIm>^1BYT;>^af))A~IeUwa$|RCZZ2TR$_(8~!7= z_@LuFcA4fTq!QcBLDoNSz`j&zk78iPI;^K>-tqZR)b$4f+Uw}`(@1EzzS#ehCxroK})0zS#b?#G+KmD!RDE*PGtz@L(Ij{GGFMZ7# z_?j~L=_(HCP7j~gna@H z9M&5=AKnVF8@yro;>GQU+5MPECBXvC9UyZLDvKL99Wg%md^p!Iq;S%%?5q?4OQaHg z#w1kLv(UpJL{A!}lZUgFZTlc1B4ohY(BL}wE?40YkAFX1K0x?`Hzi4P`1KV{ws#{C z{l{*++D{@5TFN)`w=k;bba>zQepW6GdUEpXmRDjRed)ucZbH!!ng;;#N~Sp+cKlPB z^V6Tlm{XSDaXyM!Cb677Wf4E!g*?}BYhe2~X_!)oNWAtWK{{|d_eKFNJ4oN}$))Bc z>-YEnv9Ihd>F$2Idm0?q2qrK{w!R5b^8aV1|GNo4(uwFftrFcKosilE*U)~dNkmb4 zJ83TygOF7_^P|u)*>VHWIB5xWr~>dbA~RQ$t3;0lxK-}wH)k0k1mR3Bi$E#P5*(%{cTXJFP6XKX!uToGrgo@b}cS z#oot!P_Y7dIr*qM*OZ{!J_4}~SnB{@#@)#&^Pf^N!}0w2Ad-gn+rPkjO=7=b<`M_tQnm#k>%0-u?D~ zHFh`g78fX~y?LP%=W@6I-dXO?V%JkB1%jvh&8Y=a5*V$%=T&^%>`Y7HRQCaQ=-ysF zw8g^~6qk*nStotcdB;JRku!jgfa20#GY3|r>SNpl?w6tCrWLx&!9+9=b+Lts?}NV- z+7kX(FTv0t8Ck(K_qyefrw7ztixdTCO`8?x)&oYV$EKmJL~)L;`<;UG$LLiiXwa#& z+@@GmqZmeaaCDfe_pv{A68|-ciHInE#)~;ZZ`$p?ee0|$`JZf3ccNzghN}x9xMEcb;23rF3ys$8e=@>dz}St3|CfY&4w_+$Mh=-(w=^& z?7bS=dlR`~)Ama{ID`nn#7h#KdwAQZk;6VIorSwOXA zZAoMPHO^}0$6vObg835=goDm}c#N@HD9@gaYj6P6b|vxtTD@-R$O46p<`Pu=p+kJy zb^^!;mW{}mQ+*Q6+24Q)SIB&ff6kcc&}aD-4?z-!P#nO%E5>;=-@V9gLs-j8OPcsd zKw6dBJU#SbbjkYQwk+wXIZV$IU(PJyJ;K)pWi^xJpX=SM;XGK0SnpIR9&UreByz(DTy@y^x`ddRI+PK0EY*T$1&eoLUOq2yhh-g<(uinxFT6f`EI* zwEAA03Iry)JrYclV)ba$=0rXHGX68P{;5V-nA(>B^20d_h9YlM5=04VA8pZO%6?lj zj0M73hEJ-vntQ39pcreA=irG`Mq-;wNEyL<(AF^R+%oPT)}D~^q2lL&!a~lu^-O;L zTfwAPsfst!Cu;JKg98$`xRT#TXQjy7+I^e}o#!liMKQa?wps0YU*7@pfrR?$5g>B>}ADw8sq}y z)rvk4i%Y25|9(VcZZDJKNp@9}7&I;QsDMMwTP;GF;1mt3ygO+bm>0WE&gY=aPB~*nWV9NYb<^Bkj6zI39U6N3kdg2A_?uErrdgqRMO(Gr)F29 zS|;5K2Hfk50-O=6(@>5UT5NTFg}N5wl8vL$Gf+SY@mu{EKkLSz$k|Usw+k!oOUE%s zpAB-&-?lk-=OI%IR|Qz?v8GGras>qu?*9I`XjE8K+9R?81LjVyaprEn<=0}Owvb$; z?2zTazyV5VQoNmPBleOD!Wef>+i}1ho7-0Zr91ZQ;Ei!7<{m4>p75-}z&F>_h-(auP7NWw z(~jxLWOKNG`I+uy%|2fcJ-VD^a%dgO9`(<~{Y0~NyY+iQVuWT~`dhI-E3*`-UvgZJ zrry$1HelB}`+YQSvrdvbQxCYaOKbOA|9rISqjVe9tGzTG1`=dR`s&=`Z1VBGpOSpX zrW>wb^&=@cheN6b&=syB8|LWnwuTmbB-LcVC9yzJX|>5d7esXT1)a_b`EwIA?? zq6UWC&tq7+7~>9L39bc3xMlu7Y`texQwz5R8hUR6(g}hBf;4HNgCHm!QIQVPd*~g4 zR0V>F3Q8z~0!l}EFG?q&_Y!L8Ewm5_FXw#kzVF^K-mm>5JAskCv)7t)%{f@QF<^cZ1J&GD8vLj*{W{>;_%Rt@HoZWPC!Y z1|8@7fj0CUTQST2=6n6CHb{ ztmvKBK)yyvvk+=}L*`rz>4R2L)2e9=UUwIlhpCi&^tAARnnErX}R3_mJSnE^(($1!jF()e8BZ|NEtk)q)`KCaiG&6o5R_ z4iT+jfIZMzk4I~*4}Os(9Sph!!4IzXG8uwD`15^KYu_MLW$siUR$$$_hASM1^K2PJ z92*m%!nTcb#KnhP+*h24TY?lF^JL2`f6r=c%YzRqqVxmyW*zcYywS?C35>U2CH8)- zBl`98SBcDfaxMaxoG<%uhh_sAgWw#2zjw-^?dGNYa+teJfh4J?BEQ8jAlWpN3ZMb! z5keYil&Vl(L{I=OYZ45>$gDu!lYT6)-ddTmH~vt0`AR*W_6r(M(0|4P_EfNgzmHGG z5dCBTMB70)T^=WkZuSmA9V3V*s7t2L;D_^|#CRV+OJ++mR zB6~%j&8x@|PejBRo~Yp6qVKRAR$&i8&yZo23Yn9FtdpO_?M#-*K$4R`_iQG3uXzP- znCk;?GFo;P4W=HmxegLxLPIW>50QCq3!b1Tbe1pPR50x5kO5*I;eAI_%O+mRbpN1j zfx&L?&vqOJ(B!0zCcKJ%l{`i@_MglVd~lLkR5LOx7}=nnOFhQBSamO# zu4|}a5x39&-fMSXczC<~Gb{4yQ)cbJ!*&c~k|5NV_g!8)!VvVF3jB`8oxD9lw^0=K zX>owXo|jZygfrT5S}2(Yzsn<~zkPk|**$O^VWzWSoE`_wj{RS~oV3R==;HKFM^C+W zj3mK}SkLr^K`}27&`^f20XNrnt;!v7!2rQbeNEWHXK4Wb#t((PsReO`&;V}Dw?(%3 zbx=&>uZe8~iT-bL6i_*gcw}}-;-DiX@@f@ARsJFJdJ2fdODTScMRcfcE9%>o9hcu6 zA9O?U8L}$TPZkE&;_*Y+w-z_|MQ&dwWyevWMh_xkQ z`n{_MG@ToaWB|DD(T$en`*D*faTd$?-oc@S;qX3?24qfb_0?YEPMNf?H(cV@1MuPf za|>nC`{zP}FgkN+csqz(3N5V=nXlex3sMR_Ysfm5zwBx`pN~QqcH^6vOQF4N-Zpn3{ zbXSi!P|RwZG$^uraLUc~*Lqo#m*n9A_k7C8*Mt!GLL3jmc&2HVi$FTUKWcMp^` zN|srlhWaxCCOSF@X4<@RypaJYuDbpeS`dXFcH>stbPc&Y+D>JSohV<)H<`!z<5utv zkLi4d|98);FM2VD4?I&R1NVf353)ruTPZ0vO7_IG8uj|mvV0;@C$_(^pk9?JAEqIH z3MUwWjy>lsrik}d2K?;>W5&{|lA>t=SlZQh0mt@!zn(0IQGn$`aEvr0nc`?JaC z!+hN{t9ZteBj_gUvp{yxDCkwgHB_MjSJ&HqR^DYhRGxELW6^<4JBU~CjHH7#PWzXH z2QB$n?05U>2uLO!!UIW`BqS8uBub`6zHL4O4VzZJW}6gCf2uTDvb}8K_bZ?NY}maW z!!Qktho=DGZ9HQ82dQZ+@%A=%`K4b0sk)xPSUKLmNOWCSnoA!@Hdk10EZrMjP{HQ` zDe{#c;D;Cbr@i13b};Vsg`p}j_8T`5D)IuJ>@!wSOAJG>VeW(Of6LD{v7T{QTo1d! z4`ZmR+w}IkW*sDP`nC4IK8$Vkcwb({irNY6k)1?NEd=%w(JeYH_Hh<&HKXKU7Wi1N zR;;*H=LjL?NwGloKZ8^?&&K5&rvoMAYGaioBKi^O6A1a>_Y93x-g~LqrNfk^o`H4p z^$T*rEpDL!J=qJ_a!{v$g={v2`?36BP>TA7lBUjf~8hXCjVjCOi**}W1 z)0f8ED&)F9d?74=+_$03AS`IlGv-M~8K907g{F++opjw=Z>ICyeoOZg#=1Hj5H z-mBB|(*b4;4&GOlcrL%;eyWY4Y_Qu$aRl2ALXiFWXqW5qQ2lk1a|}({X+l}=&<=5z zDXpx;Z~dh02!fnIKR==8@FzaW$uzScfXRM2_AtI z%hW_vJRtj|KifQXt#L>XAk0pnMxOhT0>a8tsn%*;cBa>NIQ;Q}wZy$C54ZOz*C6B- zkN{>)$Egr&V)OGg7&kmzktk0Ga$KUqf^p)!B8wy_!pxe*B>G4~h6d422141D@>t0? zScdhi;lA|N6Nm2^giL7LGbmlm;&g(EIwEenY+8QA|dirAi4uW@d#;u%doIE!sg8YE- zb?|NE00xa(TaI56!8fatd0$%Y^+37OqZ2B`DYeMLZeD&g7KIrU+1|>08_yu|*$68%si!1t+%1BuTbkk*B2^J6y{eEonuoL;p46(dAl0c8 zd+k{TlczgX+|!Z}G{ct=XB%=E5oI7-%#U{=?z<#G6Fexo{WKh7{C96x`r*x(B;6`y zbBF7_oX`7v5Hts%3BMJoQlGM%UT8r9on|cuZaw>^E%{?dT+{4k+oX+uDLPuXbK!j6 zS8S3t0Y{6ksx2>hW-mwZG7sj}Slouvft9MeEN_+kL3lb+_nnRM-k%o0Kz$tVCL(l2 zKp7`Nve>ITN-$-8bGp=@S&^eceF(!hDBQssQxG8TFrKNt&`!7$yL36ZB>aAme07Qu zPIfnfEj7OF?K#=9p^exi@ecAy^0~?6vNY&;W!96dTPSNy@hlp+`Sn<|#N=hkW?YtX z0a#^XCxm(cq_4w#o^$4U}jJyf~)E&mP8E3y&-K%eye_;Hf) zMC4ty$TFgO=&t1q(IKX)EV7=Tl>_*g2}{OQ<^>tjtL4yB=i++9bqQGj1B~|QIOWN) zIQGj`_DQ{kM$%c@LV%y@;gDCDF}GIC*#bBzfDgu8fx}xpyUl1Sq^q%`>8*Fg+TYFy z;CBdu9tDw28lu+l=)kPamj%9MRzTz?T_*4{pKDj3Op;rTxSnxyED&(ZUXIKtWz0!` zM;;R1B|T3w&B|QHE9I849*IQxRn3IldkU|J}`A@yU2$iH!i{rL`v*r`Uz*NudF7whH18 z%NW+E!;i`-dA@D(B*Svl$JR-38S;(*+O6pq+vu7YzPNFUn)61I8 zMoaY0D^@b`Mb+sg?MziE39b0{#KG(OV>X&=9qUVcL^}}E8-IeC&0oTl$;URVp3O-h z0++K#ABc4^bTg)?d^>-AHrE1fJ`#uNa`*V=qOz9?5q{)HXbjiE;~v|J7!;^6oGcCal zVaV2GAM~l^=-dP$%E(2|Cqy+nd>s~c{&v-8g5wlq0kPO8XIx|a;?C|^_M!Zlb6 z0pj-7=D1RPrr{iFNY zxdeZiGyV50d|O}=fvvOO^uiTver1DMS3mze{NIn(jWWGId|uop#J3b&U8MNAKahwN zWd~w~S&vKGzQtGaTwM|&A{5JF18~l>I5j3NPLpY{jodkphKGcJKr?7H+pey1I(B!} z@pudW%k%u}@#o&S?jbDG6tkk~=b1#kp0tV{Ig*G~s+af>pJ>2c?r(wIJ&Q{oo^Mm2 z?Zq9m$@47XiK&%WZ7?Nih+cYDmAA0~Z+~6CNa)w~n(H@H#JuBK1c#&XdPiBK?~;#( z_PVO}PN!USNTYV^kpMAP%!YCMj_OrXXYZ5b7;W-4@k_;~{p2aS0V=WP zB6|9Ky+yw<(O5JEqQaWIv-xCH=fYiMOw7ENvm&ez{|8lT+@_L!6l5eN+1Kc8O6e`Z zbG4;aTwGnBc`OzIhUu4fgVnuvkuoG(68znV7(hEEOR_JM zhsy^zGZ!H3a(F)t6sb#a?eAvd(zGMtNDUYBU8x>(eHo?Qf$_V*ySC-Bia{w1VXK;v zZ!mRtD6Q(#$4d-fdgJ@YB5e}wICPc~hono3N21`8xcJ&J8qwF#15D$ee&qpZ>pqmI z2;Z8m(Fs`&bG{{mC2rqj+NcYecFM1Ks|g^i6Sd|qt)M`3=_}G*I8W&9I85x+v!0mYF75y!u$$E&?G5 zOZa_n$Q0M_`(9z_Ov)}sEBkBSo}x-nPYFYI(TjKp01EXtoNW zLOQh8Dmwo`r@L&2a-nfmVRi_mzR%~rg}@~dOX0@x+xt6BNvD0L$)u5Vd5nw@r(j~P z$crwgaW(>v8v>1ct>9(CNiK!;C^F!C?(c4dgM$Iy;hoZhJdjNyGP>RYv6hlUdq!lR zgoV~sQYtEVo`THjymLg2a#okVBBpdGoz%7mWjCA*9nB6qjz3l(PY;Oj^`(M08GDe) zr{-JU2EBGhqJ?d zclJUZSYVv=lpbDgJ;5eO_dW8<6`fZc-05^-heW-BiHg`kNgt-2yYg4F2~~LJq{RoV z*Te+_env8HSSGTs-Q)j-hwec#Z_uW6B@@#EF=)Ah*3-?%RDsBg2Y!ugQ(d%WD@=`3 z#RH+^`2jS^LzFMitvjaQ*u|)CmVfJTJGKLc(RKG`HIlCR(jJ70xk`-SY?JPHX2%>@ z_n3XnMd3w=QOZrUIr_`&5W7!0L2yW=YLB-bSP(qtQTK$3=ejFCW%aVLAi$Zb`}Q){ z4gBs=d{9it7qTACo`}dLW|4UC4PGCH{O(oiK3_pXMLB8ZgB!)~ zri;?rE-3$SOGK)QZS~#>ehVVwS^Q}y5;UF>klf@H>A+RYdEz70axz#VD z%U>WTbL44nDcIs?;1^EzmxTKp(|N|mv%u!_rck?OS}Lf^MM)w8w`l`G>8)H*(4iua zygM;LRwRW5*U_u9(AwLePrL2Sq`f$Gy0WxVH*=)1D{)H{8$EhzAYQbO1t^Ed>9qMy zpSOIg-Y!SXG?h|;aWd_g1ye9iV*VyOaz9uuMK0}3!$6j^(5j9B@keG$srE-K+JC>& zSXZHEV!1CyewI+9s=x@*D3W0HN;6ZWFy)gr|oWm=Hk3DUeP>J<)hoF(Yv|6{A!WF}Q_1yp_+^r}>?G;`DYO-=O!XIOH(5!mU!IlaUrXksLalr0(nR9U z%sKuy;LQ*wDL{^6Ia~Q)o#=tEo_E$a{x0E5Sky1d&*lFjZgiPy9~eiMv(kMKTwciR z{0|8KA1waN72bd&vbnHM%SB5R$&$+ct8F9DpiWw;q zo19F&;E#kT$(GAo3COGB;{`8^G-Cm>wayt^7xm|*P$;Qs+qb77p&S*<^0742cy*gA zd!{j&3#+xSMFb14G8b^ zqt#~5k?6WcOxnn8+1JKGd|h-rDtHNM zb}Adim}Dc9?bqU|GG5-P+rjh|+JPo1kGAe>{95CG8Ebb2)M!le;igx~H)&nvqSI!{ zy)6{BiQGxhPD!qST#!`|1Rs=!p7^>NB;aP8az6ENhw75gF%}3sQ5B*1i~b!7m}$dH zeMzOd1apWF#e@yl+AoJF`+-r>gpXu7WJ=VDd({EQbizrU$wNsXgHrM)$y?VBVGQj< zgGri*)>bkoVXbA`i`KJ=a-z{-N_}#fosF#*pqy039Ifa0KK~$eDRk|&dzf;_2{A7# zw_2ukm*ckWKlU%k6X!aYKWcmT>A=KKVmN%pxN`+^zcocL{{eG=caULf@w_Mauzj_l z)mdqcq`wRG!N%sV)5QQM%Z~1?bDku!*Xnm%*XUMve@SKnDiO-nxSjiD)k>1!40o-=C`Y zkYQF$euABP+r-oqPY>Bwt{xwzpy_UEyZ$A7w1o|2VCCJeMKlp#000v6qMN_rK3nky zo#5TH%hWscKWMaJ{_Wgml^&TEB%BNJo8=;aqd6NbX`VFFPE$xeJD@U~;4qjpwSJ2C z>+bBHu*2)M+$1_6@nJCQ*}lXyJ%nn9!-2-V_S4Sf0MY?s6rOI+3Qg#?^*JdcgDw5m z7qxDtg!t(;zAR5)UugNj&7pSpIS5WztRhV@w=u1@rILI_-|{*2S0Q`okDpjl)0*?u z(S~&bR#BxO5fcsh_A*n5_YZ~F1Zd&g@N*crprFzI>ofDO0hmDgk#jakbk4<}o^`xL z72vKnUGj>G{=q89?$7>IC4Bm3yV!@ec0Hwe=S~Bc(FPWb$XC(}O{+zT)XnD|E=OJ( z+^BH6wFs$P8>`$q2t%n~&SVN0^ryF7F#dn^!!#M3!06 z>(6vJdFonIXov$Vs-hGH@3_wbS;Ov!HkHu?Su_Tjb~ruSLnxh>wAT4Jq{vg%pYNSN z={csgqFhk32%81ElWJhd&KrL&+K+mFCCu&k}KSZfAP zF~u`g$mYmogfgeNes7;PfUX2_8v z-)1EtM+judBJB`s$CpT8m;Ad%hG$ppWJ?G69Xs2;@Z5H7YvoU|Xoe0}C`;!zH-+X= z{EQDU6uD}?lnPXYO0O1_0%m2M|7`R^z9{SCx3o;9Bx7UX*B^3!k` zDXEM&bgcL0F6YBW0ZTS=`7w^Sx2}`L(80Fg#(8mU0(-A)`es z1lD12$+vD$=(L&l!A}!$V2buh$U*eAA3J(`4528z6nuh@kK0jKIlJR;)rHb;HW`(> zo1=2XrX#zr?lxI6;eVH_mwp$e*ohrro}crK%de|j@GpFG=$F^wWbOBSEY2-k|1QND z@J~2+q0xVv(}`>YofR~}4^z2oebr$a{90u>Y*P&cWcLrK?khMIcIYW!u5y}t=mKMZ zzZz6FGd|BV7vPziXu`jaRSDC9t^~WO=uR1JN=b*j@ND4*mxoDdzN}GKnaG4V8GqPC#Cn>Qaccl%1*x9 zhhc7~Q{1WY(~JM7q}xz3mAA8=ouA$u++=<~YO!g}r(N19z3ex+7t5pipHnMdG1VWJ zM{334w#(mn!r6C6Dx{r8d`utxhZz44#ohlBIf}SaUpA}Bu=_dfj0ezCcmO?k&(`H1 z0B!LvfUaYZaaVqsFq4yK*mRSLwayPmN;B!anzIF2@L3 zeCK1iH{$V2_cP;jc=EleI~9}D!{5Wu!r>4!c5U#dZ7h0+10Zok0(#^B#9*9A%C)^< zQAYJNi6=$yxpBffT2Lex^2*>!7JuIcmJ?^>M4|nEEB~aO_@y6(=7Tt%oOUzBqL;9X z=RA$rWFfNr*R4_KDTMVle{?h(BBz=FC_6NcrS-*vMFAEieT7aEi%{H|S4q!aROw&=bHLh@LDm zM-Qx7L2YgIeiLc9j#>p!#1T+uz3C$MoZTQra<@OVTZJ3|p7|3>nNht@I5ae2>1_EH zN&Z?61hjh=MHP)6$kN$so;A#U239ubklE7;jFX#HwC3nH6VW3uwozuvV1@z9dE7|} zFNv_{0~4s6G$X*E9b|)IOQx&Ud55HeSc@+mo|T3MkB`SCtPLobJaJP{j#d}B6GS<; z%n2jNA`)y4u@kW3de+u#y**3tD=UmS;LvRpz)|><;H=%dVoVuI#?(kK>fh$|uai*8cDmG!6AM5i^g8CvqU5p0F2!^VrzuGBZIw|)HlECBX3NzB~nPH77F(afn+&M6f zC3`8J{0tbCg#|zNU+i~QkrXe=vAlo1{;5^>uuPQPs*AMxT6 z-Bq-7bFBg~E@oN#opW6>ds&S_bjpp70oofrUuwwv*h>7U)aXJ^E48N%9*+hyIRdLbh7hXlqP-^tknF;bG5iFF_#$A&R##t3K#qU_)eF+(^BH)`RSC`*r|a4r|F4x3vasN`6=Y5X%Eub zW2u$pJ%WmI>i3XEW(yIVxL$N6@jcI!SfRINmWIZOiFG)M>J3FnEceHj(DPrbQk@uL zsix3IzrC?&w^NlHl*%&!yByp};d_*Ve-;XN^e|Rk8Z?Fsd2>A-A2L!&F!h1>(tsyY zp5?XD5_&Ok$E*|{-RjF+SvNY>7ls!$;0e-R*;Y)^KfHXW(X8VMHiQH%Afe1=t3c~3 zPf=;r{W~@U#0zT=Df#(Rt=5`O3(O&LP*ay@jr<4~yQ4Hw9^UQ57@m%uX!z?}uPL^A zj+d2oy;Y|x_-k}xEzCRbHSWh5X;KkjZ#lgxu>7%{>pGMJ)>GOgJqk$}DsMZA3%lO2 z)(RdvZjE0m)_)i`d_x?T6SVTQWpOd_eCw+8=TEDwXM*Xl$07<%Q^+gr6f2Y=f3uHL z1f@ZvP*k}|>sd{X-C9DYtb-8kW>03JjsFG>4vrF!Ilv6=eKI-Qxx#x;GuQ5bibhaR z!O$bpvNorjQ2m^}Lmd7a^=!cIH~GWcz(<7e*e*R#`dSC(hIT%2Q#TILvI$N_fkmxo z1-=d(DaPxAt^P<#El$;XeoK4rf2nO%{;l9sMvpp^|KyM7h0I~yptl5YY*{S{df;wM zW_!!l@RvIn1$@|1TZkz%i-QnGH%EtFf5*%fv{+*{c`lm%g3JHmaG(`!yPTt&TmF0i zO7u&KTPl&4DiG$o)nAm<&IV078`zr*UF+eV#%EpV1eExKmG|21f*c#^&idNh-^=HG zCyF(Of?Gznpz2>c22!ZT4uij?uvA*Z*S7*Tyz$ zbCF`K8G2V{53bGGF1JmnHrjRzBh5bF3?gpp zI#K+4X3;%-m2Fx|>rlBK%PVfa9I*b(1Ov_4{x3ia!RxdsEiR4_{lu?+y@u8ps2J;3 zeKoKHD>n`!uf76%CIQB|znC+`Kq|lu?mur|Q)_W_IDHT?@x%~Z52RnxOCRNV{&Dy4 zQ1k0@NJ!XJTrzmAbtR{9OEKE{Wm5MvRYv{~5}RAH1Cosmfh^+TbPmwtigZ6>FisY? zpA?2o{q1vn@j?zgnOkkcct2n9F-Nfg#UnnU;P5m}ritWV#&)BPVXtI7f2JdFYqY_e zEz>p!%U4X02u&})!{)hhxi`-51qgJG@TSW`CU_evyUBrySFfrpN$Q3_m)3Lh9;^EF zs)tvTF<(bGs%dGA3mGFTb4NR`n=p~Lp(*+2ynPt5T`l%cPq!ws=uC8P1{XlTn*x>p zzPmdlHWbRcy(kAzBzBhJ%e&r#5PS~wbG9_dyOOoi@+*%flWr?3V`l&Pu7^C2?iQ(w zi_1t+dQ;#+5uBfPp&Ej@!AzMjO)8S1^_ zz8SMz(9QFn{`hsJ+f&}NeQCO4uog;)vk4XWCY?ng{+bt?V3z^C9YQcI%l9k4^CTi) z>^>!~DXz6GMgq~BJ@38?#oM`S_fPz{1NZz7f(kC$ z#nV1f;SD*@0*MaQAJlcdEOR3EnkKNTQ0vA={)ztBQ*Fw%>iRqCV?Y4mexoOUw^Vrh zbfUK5v01s>hcPYd_mVx<9|}T1Ii1BM?M}XoT+fqT*PP_<=2^~7k3z4^=(z%@&z8Pd zKiGNis1g6-^(PtlUA}%O0il+X4NG#;a?7R6mRV@NU)hpysX6z@jTfPY>9o_*rKT2k zYgfguyxs0v-UmmlohAho&R(YZoX)=RpVbi0q^%`SXuIA!|7H72f2?_|?2hK?UK=kK zz;SEEgV&x~i>lfn_~3im1Hw*XhTsFcwUF7V>U5LNN+PbjV{<(7uW~|VlLyi^c|GjXb zDdbeBrp};~;gyL=on{{DD-=q!IluD|a4qfja`igXfy5n4U-Mb!Ag~p!g!!$4r7As8 z;BExcHU-#-R8ZJ4@2JlH@ym_%XW)v-Qu5V@(&znAH{X8pBqw;-Wk$5b_H2J4zcD*s zovI{7w4H*@_NK07&!e$*x#N_E04w<_Gb`GcsH}_pmCu4qObu7ow<9IZ7pThvlJ*$v zvT8I#28D-Dau-hEdA%KqrebTZ!jbip7B@zox8$Jf62))nMAOCANu2Bb=*sU&yWlo) z#ZR&+X1^y|WMt(nXJ27)X#mZ42eGR$nac_8M3)nCHG;G}C2<1hHdFjbd?@$+9OIEX|c4hV0j5M$dYjl*G|iw z9{(MMKK;V7L94u~Jyy0FmJsYnST2j~JsC*J`6OV~a@Pyi${!x|d3Jan>QpEK<{uUG z(8EW{CpbN~>+&H%^LV^3+t+W$B>Sw-#!x5oE>TQ>&L?^(7xr|&`J1`&*|2_tT`1lG zU10PXZ-9>HzpjvjcWw#pJ%RW6%|3Nft9F^83)AnxxX*%%hQq}bWs3?hBgm`QT5Ufh z|0`|!Thz|9d-nSvaw$pAEt(GlwZ;fMi$5sa`(N^>Z4(>HrBM4|wNrwpCk;tU20u~| zR|osSdV_ARAFY6n4b4G+#(G*xHu_Y&E;&(`LuUKf*b7u;qh9DzT~BxC&VXNo>dsZb?RZu`*PcLEPnnObuy{zJmeb#n{-j~3wX z_jV-krqbg2I?~!NE#&3|@%<={E7JOZv8XX}m*_lESxgWt=$1^no0>!Ho z0IrH4Cyh?oilcCvJadm}2p(X}(tDRZw#5$tqC?=xWtGPlNgS{-L79&c2j)&|WJ~v8 zku;gAWb}HsGIa<{HB^~Xi|c zzY6>Y6(r<^^FDaI8O;F^v#h5cZsQieq%}=WZZ2WwD`JGvjchPO$R4QouCJR|O&+j& z61cbBf+$G$#-Voz#w`e9aGyQNH%L#j_dRC_ZCaR9@AicI{nT`~X?YiaFn?$u2yrnX z@SK76rY3FnT)NupwLc&OpcwrG20Ll~%-akO{WPcFO9W%J+hvH)XY@T6mBQ94IF6<$|1y?? z;jgm%>(!XU&P_q&6?xD|*;?Npsy9O?$gDXiXFRnZu?$R~nm;`H==*KD#w1 z^fHc+6SY15=6AW3?|KJDaS3q!P`+JX0x$A2y>G|>T1tI(!h4s{#(ldq&1ZR7O)k`? zO{zoRJnqRrl7$Mz`=naZDA!qbn8L*E=e|Ddanw}-E8Ri zESH+x{j}e+^X_|rrp;JVXLnS~t=QjWF~2a+ZjoUXs8k$vg9S7{xPFkbDM|HXd^8X+ z@U=8h8&NR#BVxkp=fO1eH{gWIJy1!-t}eZ`JLARq;6UHsPK>R!QPFd}zsXb5^^zlD zT&F!8k*(y45VG5?@r5XJx^zI5f(`+IMOmp?g{j-sY+IYV(76M{`c^wRu0XxsF$0z4 zqW$=jMeU$-pHvFmtbhno#K`4+nepR3&qKsxdG)8_nPn|Fw$6WF)J9xu1 z_E0OAFN*-Yr&?%3T$92nm$#FJ|01@o_VA&;>f_kJP4+l4K0(Rj>w6$vV~jNCjxO?p z+xt5ub^*K7KjD8T?S@9i7NU=>2w*o~VI~63;f-M92&NE%om;b( zv?yOrss~>5P*zc^`tNR?pIVf*OWu=}tG6Of9T5GV?G;0kXQ85?9EU$Cu5Szm*xacI zQ1O-YO3VEuC{!a4&5BrIV!p3q6`hZ=N7A{P9|QVLvL#~1Bs!viR^;L;$(Kl%YtcAJ z#D^bwDo3CNZFU-6ZBefD`Kui0#|_)6Y#JECY{yrk z6SKIL9F5*tzlb%$ocZG8Agw=|KrP;*O-9OIg3R9#887;7C4INbrP+8o%MAkxTwEm@ z^dY5$9`_jC2hnQN>0INj*#36qVaJxT{iSn__Uf`QJ%WAILLedL5OW_TK3g)LgJ_(- z58J_n1}U2+^IVsZhGeJFz;mcxh#36$QIAJoqSsf5zG-8Fp;r!?4=r$C5Kn|vujNbH z{!_YgzcMGZ+t<;jAm=TR9$ae3_i+g3s!{ZjI^izO*6E0b3^j~#cPL%is4hs(EGGzu zi!yzVu<-9EQb0GA+nuwWKN#>plteya zndJvp?I*5(hn^2)Na9+r92$-zalps2#i;We)vumyEt@C*N2G*jQT<3pr%@reeBk}< zKeYzo+VYWG>51CrJ8!AsT6A~|=*J z+)U7%W9MQU+k6*ue-`lVcY>761r&)k3MVM*j2nZlCkav7a5LO(c$@xSS0^!g^E8w{ zeoe5tll&ub;6<9h9kG_CmDBjRpFC+X(zjCzFEgU4eh^8*OXx!+;k$}WB7T|>dq}3^ zGVZ)PZIM_-KET*thz4lhx5qD-OftlL623MgYkk!6%IS?9F@f1(BOn)|PdN&49|iQ) z&&b5qK78jWhKyEIC5y3SJ^BQg`Afy(4S$^MI+Jo$NBG>fG2JS}H0tXjw}z0qA7Y^? zaDX469^+6%u+v*9`9?5~4{$F|EAF|D<^r!EBi9%*Oe6lWb!BAr(0nS_oZ6na?5Dq7 z7w$jjTn=Qw#wql8kBa0ai){$m5aaiEt*Wa}Ma2tS|N8V)GJJvP5%JoSl3UBZ4Xw~` zBjQEj>kBXSH5xm4xL{&>iic?8*SNt+7z#fd_*V-^j+g zLFXml;>h;_jbY+pxLt~rpZmuN*+pSt0IJZeEUmFX`blC@rN15}s%LBT9t|oK`*8XT zeA*U>S;ZpbHT`={diVCmc_H)Pj^d#LLJP!xZTI(KBDmiWj{U! zwO;ev%?R+CXA)2>?Qu(=?oc+%FDu@-N}@V>cG&i|n$x30i+os=aTKF~ei6DlF(EGu zJtW&zIc?RV8Dyv>uR3;0JZS)z5x7z*yY7s@KgZc7ImhuaJY4kDM3i(42bq=p`Rahr zedr9t-^6q&ui6y(VO*i-kRJS-2@AwS{`qk7+vcjUC7VPDMvI9^&bi0wu&DbK|;r+gJ zg~POa_+1oteBv|5Fjkw!(ddO~s@fn{uWH|m2Q_WL0P@|og{FW{0j6fI|E(h8B)?uo zNr2RwPnK??^W3ImPzuT5fF(fB{?AV>A36i~*w8AD6&s=1w<6mzmh7Yls?K#3Wd}0g zzMhMx8WIlcI>tXQ1y~HnSd7cQvpQ#b3+(`{dhS^0eGO`xMl){Z`POXbU zzrj1=6|UDX4gGJ|f40T>`h53CL|F!#j-=Y5H0Y%m@8zj$6|Y`kk9@n#gnrsJd-N$X zx)8B)ojxX&OB<0bJ1qzEU zj9Pws+?gTD|I1_~E#sclKUmKuiuuWMXw!?7Fw5;juI&G>_-F#LtroaX*7qAtvu<|E zwddkhEc`-r{xPsIqng97*S-f+ynZ*9)!LU#FFzxlwR zz&3M8&HJq!Ks_=xR+>3$Zua#`+1&FpqAqpu6N?O7$X&%$V5&VAElphz#zakpq2G^6 zi4-V)hdJ0~tN*yvHzCO?IGvaMf|tuu|5?FE(V6X(3NFALyr6KUQbFr3?+;L$26F0@ z9W;p%W!Xf9YWK5z(6A1>#1rkNb~oi4%XO8GS=^5^UdwA5C1(}aOvqdqJ?d3k^4;ro zUPc^;!tyCYlP#4+?e@3)Gc_$2+5N3*-sP;%4WJL%2;%d#puhOyltF^5+?p}{9JK>q zCyE{LV~zF0<*sd*Ek1A2r|XQv9-rO#Mdq?K!H?RGSFUK4Bi^6!VHRn9a(j_&Q9X_; zM0#i7m^{MQ^?uSpe(~SA0h$>j6^xiCvu;PCs){W?%h2T3pB&=6N0uML5p5JG#xkGv zJCtlPx{SEqz>g6N8$rW8qG&FSQ8&P zYLj7hIZ!RN8xUf90scxP{%7_L3&hUmWaGOwHrE zr5d8~*~9AoUXZk?j*dDu1Ezq+~GrEc8_&6;yUjve2A$E{yw zS~}(Q2X@;RPxorB0?c~lrpr2TDNDbdE6IEnF_`BN@^H;+^NEHu6*Yo@PK$^qk9S%s z7HtfwR9>&fxuoqRtbM)XsI3(j>6h;Cg`#i%E3X|(&vH*iozav8>z_Msj-F~HsRSYY zHK37<3_XbU`%|L9=S@pL;Prmj-Ly^zA2j8iT*dNS--BRGkQYcHPI9nF zWiWcDsPA4d0(_^@Xeg;@_E3wIRq2xZ#%N5@e>>Z zir@0p@X{H{?gy!X5yQ999j&ZU(R{89oa(Hwwv5pES;zklN*b8zPtYm?b_PlgEB}=P z{v%}=tR&28yOeXmRsu6upFIfu_tAY@GpaHiyEclz>5jzKwH{notxQG!|1`}SBo)B5 z&XmZP{7|6S_8;k`NcTQanj{FQ-if@*E2Gk}$NvisLWcK2OCDUJUdAO$o?o3ibTjXy z&Dp8X&ZClesucNXeSY7OO_ls$!N)r!=NzuhH+uBeVNkTq`WrNJtJ8@vj4s=3n_-xtWA_I`lF>9BdoE*K_{xDY zs4=>!VOdJ^&g0A{HQKEvdC5PxM-Cr8RtiJjGkzQaQYu8Q>i9^r`d={NazeHn}+^g4#P% zQcAoTINo^rM$Wd?V_2>ei$2CESu_FUf;CjyCGQA%6b!s(BjtH}*>k?l_+)gSGJ1GI zy7ik1?HL(ORyLa@Maa=#w`e0iODV+Flx?s{=oiLMS&o#?pF}l4&N*iNS0<~>kcS$ELgCF z;1Jv)AwUT3!QI{6HNk^RaCav-gy7JPyIbSdSVIHf$vby`_q{WB&CK^#uWsnGdYw~G z)vl^tyYAGi4rZTdey>DXN(HHyaT@r0`U*X7^2&2T6(%Jn@7B~8y*lxe1qkI*AB*hb zn6;V)E*BdIuZ|YiGAercT0V4WoBhmv(O5m(qdIDEQn|z*#U{YUhA_t*wR;&qF-_K+%Z7ksWESNV>oS!p8ks}z|9iC$~( z8yVABGTRy#y_a_T8DNjnh?!DF@R{Z&BLl1UiET#9z|uIvfTo_=3V696bH_-V1Bq)k z68no7508;eYdGc1zCSc?o(J5UWaX3TH>&HSZe2-2VCj^jrm|s6!IHM8Nz1Bt?N$g{ zCzkt(gQX+wQUi%NgP5$cK%Zl^BK+lhv3UHV{_kfeKU(nmv^UdJ*b{Q!=9d+X_@3*lmh3n$Z^~>}=Hr1A;HzNI>-1*mN zA=N^&fYjOyRA8^JT#L15g&pbeE|-?*Co&FpfAGWTJ-Zs@IE^7;eM)=)@~Myie+ZRvbqp`gdQf=G# z6&XcguSSXzjqQC+r}}LUM@YKn6P{d5cU)c7vyivhyqXA|PP*RSnibg1uHI7U;hO8N ze|P`$leT-eDXyIrY=t}OEjDfW<{l>pTBqbeKqQC1{#!JAJwFM1VF@d1=6oXp7ulS4 zv7R1G^^?_3y-eR*3ynI{C}8k%xz_eb6Q!a#DZB>;%Ql)-!-X8&_I1O8?{6j=6fo^6 z$+g%%+aEe&ar^ z9CNtQLo4B>j+zyKKN>^tw|V#0#GoB7K@Kxk8~4qmcvjf9kty+B?T~J!*&}&WodN@S z>$nAqXND82mx#pCzWe2Dk&z#VzOQ?!0@_>JRI3aW`-v!lg@IK-KC^#aC&lRd_Qaln za(p)|v7PQbs$OBpP$z)M(v2z+BA+ZHyKFZpo%tYl z5B?XHsES%ZO;P4|X^}@KakB;3@Z*;SVc1~q}8dE5~B=}{`ZywzPrYh~N6kRI+RZ*!eUorcKAG~Cf}I~-R8 zckVZ_^SUI|y>$i#lO2pAI5t18!mf4lrJ7{t#i0|UzjH!syvYlCbS^!U&t#u=(iPmPTsLnY2$*q74^$N&Cjn>yNlP( z#wIawj%;dXW}njNH6to$b|ZJccP3$%)a7uyAxhwE=<}-B?1?Q)xIZPS)Iq; zZ|CzMp2|e0)}4e|-SyYR6rhk`S>2!&1F79i1fZ2dPyr}3#z;K(8;y(^6FR$W^NWG{ zRYr(N#>mO!uTSB~sq${sBozo7jQ-3<&JGMw<^bWr2VqscS)=Y z=BcX>``|3@)U;bKUF&1l1}2qcV~RU3-5IV!+Q1u*%t)kT4~;$#nVfS>3-uf5d>71` z`J~KZgu=OWkmz9LNh{EXi7Z5h`wq~JjIGzyKaGqWL|^fbk$#j>OgwdcuP|$|>hDbw zC+X$X?)+>7t(+nNX8R)YP<4h%P%Iz2_aFn15g#yewfEhPam@bya^haAlR5I4P1s*wjL->? z7P;eKZ?w}JAGP*5>8ah&yD!U~)cO~8xJ8U$@C+dZH1`nJ+&p0_lCn5rq z6Mqrgd@XUZ{mm++jcCNF8pMZ^R|~kk*ETF&1HjHt=Ht&fzd1*yZ%4c)sb8?mvz|xVcN9W5 zvC?BPcJVCjpR;L%>@hn#hK%-qdUE={Z0Ib%0hEC!-f8T4YsPkp6|^5*B;%T4B& zl*O+0Ikhy8Q&CQ@cd3ZG%5<6N@GYt@UkC+z#y*36X4D;B$%ML`Vary&z|@LQ+)IZI zh3J!#OFyThnNyRwL&`t!^(5ux9Z}Mn4r=ExPMYHK`Vs8VWxg=*V7HmF`$4lGTF;WuP zOC4{=7%+3@F!E@Q{9X+*6@|0>t)0D8MCbAstijzRX<~J7zB<9F2O%RX;ALe}BpwbV zoZSh3QoXs-(5)(>=N`7>bL~r4lXCuf+{trc{uj)%V}Zq;b|=SO*sWB4y!(5Vh_B3s zxN)YiR7PI>lBD@NzrxsX77r3$7cVc-ED=wiB`7b1QWQ5S55)1~Xwa)Y9n21jW{<|j z+~c0q@h)`g@o-vogIOBb`{r~sfd5H}(J!HA<6@)qc|4poz8HL)tT|cH{`aIpMLQtL z?LNHx2J)zl&Oi4TceP7NVQs+=j>~=;=J&;0`E2lLTF_ z5^^M8#RZ7KjHEg*)ub>RIv$Y$M*xrb4l23)4IFSZfO>IhV13DhQmXZ5UVdr&<>Jhb z>D`T{LDR;sL~6{|+RPc>={G1P=9DQw&ibfsjxA}X`=pZno7-FFibxwjLiI(p)TFv`A>(G&sU6AR&#$fNQKkq~}7eR0(2}s0w>HO4@3AEI*QFUObQp;J`1*g-ue}L~= z7XVPJYw1rJ)Y3mtayx4{^!`ypi{m}U4TZGA%OdBC5Nben8rr#9<4^+y-g-$K8 zSxOy)H%svchSiHN8C}CVdiE?ua$;=^gH{qkV#YCTkxnf#TJdF5hWb})YoP6o8I!?n zZNi_VWOl}H&gEbiOIy}!tCLO3Pxr~tZXwwFOgK18yUsLM89`Z2D;SE~C2>31)%MKM znTPM1tL=S3#6#-UR}IYGmtamSkn`0q^LUS7j(eEqdgG3?;?^kdLF&p++1 z|0eNEaJMHjWNek2VYh*#9U`%J-wPv}JETu^?9JCgJ@XM987yCde1*5^rvfxm=; zpw({oZIY5SMRn27}?WfhZiFS(@Ex<0o!K$W7}4?Zvgtd=r%qb8i=*h`SK!&X!p&+TdFBKnvCce z0k*7aU<-qH+o$)_-J#~zHZ$rv{p=_g1W84C6lKzP%#Agam4D*g6od-X>Iu5U)=ed_jyD3b~xFO-u(5 zxn0*h1-4ZCSv4Q^MrlZ!VDh3cAVTD2`Cs~Ydw=NjF*e(&ILPD8J`tBkI>=$&IDxct zNBGYBM3J-(5EDx)rmflcnNrjVRb=JtGZdh=Z*T-X{P^Yb_U!``dDEn35gCUIkCq%29>eFe}Jg} zv>#Dh6m)jZ<2FUj)k_wOjuYYd!M`Sv-;{L$*;#Ul;$a^By1B>hkBLjtPQ_ouNmKt+ zu^Cd-w|sJ^>NCc$yt#GVH}g*atJ@*PN_t?OA5j28R)|o&Opgy5e6h0#(tAE}ZN$pl z4lk^M%nB)IHgZgH)oWd2w_d$Ic-6oc zhNF1@!o}W30*vDGQ#s0uJJ9zle}biQBG9EhZd3MrSngznOr;6;1M+rR-7tVPUvXY2 zIt|#6@3fm@049iVjK|rmLsAss;9CICcYEQ3G`tbIyysL|TVVFuT4kAlG{r7pDq@eJ ztQa^NTMGKeYuXPCz;Yt0QN_@y~x-eKvq2 zY~c-o=c<~s;gmYeTFt1CMSNf=vgprnIM2Q8`W?cE!ycQT$4{N2--oFZ0j{{7eOAOP zV0EZr-tb$fRORf;kcH6-VaIk!txdnT2V}w#`#asfJVtlb6B^H(ISxQ;Q&IZ9OU;Y* z?BDDK6|}2ZWElPf%QdtR6ys-5fWw@aA3-A;EA>#NU}7F3Yl^9jc>_yVoKH8=O=d>l zTTY@>Ylc zGP>>5@w2GL`wLt*ugF~+nP$7rh>kw%lzTx6YQWAq6kf70&dCX=cZ0l-aL9Sg#2viB zTTtKWsAf{47Gg$4<-&DSP& zs~(#TV(r(m@Owkw7>l$^cH-kW}ekR4Kvv+D?w$GT#% z8RbVon-RAoGuh<=5N=(uQLXXXw2Nk!QJVm?%{Ue`aFYUwg=hO&N8yCT$LHMYF)k7gA7uDl`7N z1_~{2=*I2^9zCV_Q-HQq(C;<)i%NXyKbhB?Kj&ufyXrbd9pZ%*u~zA?O1hfQF#pg~ zUxatx{gS^#z5$8{h*f})BsV_L;m=MYNYUaIsf=9xo{KBL$vPM1-B+Ie_KX8G*iX$S znMA+t-S$hE6HV?NvY<>NMqA&qeBhlYC#4U<*;yT^y--0xwDR*_r)HxgHi)&Li1|oK zEt}fDgJ{eL7e(cV-b!$?Z%xv?TS@0ZC?~F$1cx^Keo1J5DurN?3C5KfLw#s4 z_ielVlWH1vw_v1R%b9w(4MGZuYew`yG98oYEDwd`Xey#fZWp4s%zNxVk^ znxBxUXGEht%@r9NfWs|mWzR6#T7zSRoTsOePVa?x1HVaq-o$JgAZPyz$N%5FD}ILp z&{Oz`JuVjON?o8r*PnWlm?x@B7;873ClZVE?HMXlQKu?YNqR+CRglxL~@#0 z#KSU46q>Oo_usu5M)1(-IDWZ4RhbcbUNf*XU+u<3;*Kyh9%@MKWF<^h>P~hM-R0aQ z$)v7}Sph*>N|i765-fFZ69R-F&UTkH#=HgDo1%r(qoWVpUqcn#f|`fP{9V)j0~zLt zf*-Fb@-0qQ_DjYDXMV4FhuZ+cgwec=L-=0;F@ZMvOaA3GxaN^K067{UOi@+%E|st-TF6n@Ym2v_6~juulE-g_zggnSWz?h`*#2>!5@!@S<;JWB0p8q z1S)=@Ld0GKL6Z+}nnKf&pt-;egi=GvhyY<*GAu!}dD09KvkA?SPkN$)V=< z1U!-uB>MZNN5qY zNlvGYf6Dz(GEvwRED8**^VNB^0}^~r)_fZi(h9@*ohfWg2t-h&=RrJ zADs-pwpa92*aIps&sb(l)+2 z>g6!XVOyMS#zk5e?M|A)4Jgw(e6XZG9~}-AXA|jyz4M>yhN1it0#oL6!wRBUrEM^$ za*+~)q4BX^!a}B>fs9mSmE_%2(HCC7zK1D~`GVpnz{#=R)3*2h!ZEecVBo@y!jRLY z-3{xVvMGcCSIZo_Rx6gXh?}Lm4nv0eSlFX-eGNaw!}ETHfTcVtk92#93SrDY(aiye z{fmY6I;R%=ADk5%N>V*A^Dqt`n!{pcvcN7s`_ClcPoz?bN3x-~jdk`to-Hl;jHN^r z1f^;P)S}t1$`zFL2kEM(*5QUUo2u9TfctN`>$fxmY;ro+A4xkLp9p8!KXb@GD*}wbz4-`Uxl_y}BuN;`@MyUcHJscQ@V{ugA(ICC&!?mJQ4uR5qz|ZOjXHrwD z&}N);!Hc(s-1^8MDc}~f-|GB4lrl-5ry&)<6gq8+kp=Uec%f(m&^3!~dX}N5LOid0 zcA-3U?WLr0){sQEq&JD+1`{yQ0o9#xiB&WV?ptB1d@$RswTTELdkbGcXL|(%TCUAnjIAcEhn1=K%|Gj8HMUcejcZU$Nv~>;r#9!yB zG{b>1DS4HjrM`=dbRZ@T8T5FT4!wtM_rP*4_!3Oz!qg3TMM%kH)%wcs_>Nu;x255) zwV@XgSe(=cDqLDe=Vn~@?ySuxB%UlaKhcEbc0^Et5g?OaiB41yLAX8!IBkSttXiZ) zirJRy-khQS9UNTCmcomlw;}HY@Gw#sgx=9_ZsG}Qfo;r-)p8q34KPNQ8PsV>R9JRs-oD8JvuphC?Iukpb@yD81z#g7@`o-;Z#ZDE5 z8PY|MJCj`rU}0UR7c}ZE0Ezmj-S1# z!sgdDtcMQlDSf?E6$^K~FD0TleeC&33Bb(c#qI@A!&7x*Dq z?T4H}!Pk>2&B{w~@@04ozc>S_?bSFZ4YK@uYKv zq_5e#jd}GvA+vwNn^p9DBnEgwX;>b2(4%N81sO1DhF;|7q`rSst6Pk6Ba-GheMd)Ar?EFC@FjDBE+dY6yE?R zLLifY;$zgWs2!WM2#O5Eop?4i7AVK*^{ez-3gr`PLndzmmOUrb@`8XTXj=m+1awB_|ns| z@lvz{Qq@Xiw=<^7`VaVDUf6fU(EY|Pz{6wNz|)J0ikeuOBI~||7I_v(f8Z>BakXxi zSm~ucjP{aY`FlZzUO2p%#8^D;a@@hkc%Yy>u3;HJr2>${-RD^WfRC2=k@p`Ro&juY z2y64y1#~=gC_xb*K=C}=L`aw&=s6i&Kv|WoPN`e-j^fiqco+9dbx89z5gm z{J^}SuiA`iYjUAGpbf)t$&60KO`9e%V7Pa?NB2ywmpY1sArY@K6E z2Q#s6c$cvhld(ZDEr2qpp=5D^)@RHS@{FwNS?T94m~v`-+j}BBBKwC#*C+W|>l!6~ zKl|`;K=Q*kH^Ck9l7N2mWaBjsQ8CS*@LX+C{Z9Z{a1mPo_@MtSm-usZs=B%;?&GxY z{2u*D{blj~KuZbfEcc2R8c)5mc#2|W`YmrzmiEwI0K>5%x+Zm znn)(0wx7CS4qrOy*o-hfnaFaY3@K9bnqffpX?;2|+B4vXe{gULF3N5&o!sK7Dc@vJ zg0W|z4wvlEET)P(Fm)n(yaZ zO$tdK-F>6VNI_7DlsRKzA$|%f#EUJ{%0t4XP*y#wl(khh1A60lW$<9lZvQZ{H&hZ1 z@_F?DZdAwG4#flp#~`gj_zb3TzLnvu57P351Tt@`rR`_iWy7$=m*9jY+%x3`6$n*f zU+bM5Nh-0>dcfYY!B#`-(BlDN)-G6qrnhb7rbb4M1;nvqhJ*kcP%OW+FgJhJaY5I> zfW^ld7?haO*sJ#TM`l9Y_`4(ZYbKyTV%SjU{69vif28}Oe3<#Gp3jZ$YWmWWq=N2~NckM*Bge3=u1szU`Z zmp1G-@Z>MQKpA26RWP<9beA};^No!|__ z`qqY%9BVwIq71Bs`sC@)V9c7B)`6M!#hPG>iZI6$7i(i(1ygi%#9QSRugEb>au_V* z|NcFXmMI{k`s6FEQkX%al^L)F_X#b z_0%_pBxg*w&N50%<+Ql9rToOOJ? z`1&K6uIyR9mSQZ=q1_1D1#Q2mp`cmRiMEkNXa{H!WH^+O^GaiOPA1#YkSW1W_U%qy zYUSRpvQFG;K7dR+SVKyo zfdU;RiI2Q0Z5J_nX3%|$pH2zD+gldW&eTjDRAD!tL>WaY{hHqfDKC9`OJhGJFQ*{j z0u9%01(bT}wgP^%HwM-}&(?9SIXI`gVe{9w-f>D;s#FJUOuT-m7LLYgourP7P1~9b zAq&c;VT3-K9iIJETlgKZ=_Q$1%b~fXRv5AY8g$u>!hSSbTM;6Y3R}W7(H}h z;r{AioAT_C|MhVBWY_D|HVzY05SE85BL~>y`30!0L`u=-=`G9?QZ8`)`nE+Wehl=b zHIF+;@jsM1{_!INLe#p}yf1)~3YO5*3IIwtFjl`@gFh8bD64`O-MkYPrZUz?;}WRd?fa^%ZmDBQ`V1+XSEG1Uuq;yvOv6i5)mK=#j{S!yFXq(BMig25y;c@ z`k8_oO+3w?kK2HSpinFZw`*s26uoHHzcaSl#1XEYxUd{7_zc?!UQFkykm*SBLf}KcD0kU4H|F`qdq)iZ}TsR)%-1 zN#;O*^BQ4$y=pnaXTn8jh!?A$-2A3*qrGpRDTq>bUfm&A(2|$A_Wt6a!#IN4_8VcE zgyF{T#HU5DmDw~?ipbJ^>arxKgr!19O#00X&b6OXqK?i(vAM~I?B~@$bFDXD6xdVP z-fBX77p!25Gt{00%8YlU&qxh0%Y@d!g_{?nIV??=ZD7ZXy-QFt9d~SFQF7YT3_1)* z@?~T(>VlOjC(1WPeOg{WS}dNl8vO5W2dED!cMRn{d1g5sw8|VBdfIEShO(@Z15O|7 zqYN$~C)rQDB6YSEJ)YswsD1rsf(RZCHe?wsOa%u^Kp>as_`g+4{u^S`cK}Ei!nc1I z@9$D72!MJ7L-m(6xc8duoFEuoDl(dro4b;<5@xAm&MSg764O{>Se)<;c^mafsmq8) zJ3~R2wZ6zq&Xhw^`SpinJ$iEHtb%+5nujwkpo8+{N{!N*B;#bjzF+bA3)dPr`=`V( z%yZH1!3Rn@g{LW1y&lCPNbX{$0aB;-9mC-Fha^IaVtUFe%F& z^2Dv-GPsbtrX_0~G+sX?@<#R|{F%NIPhNrZIHjbaOCaO%))7+so8IkzD^&asr~vCv z$7~lFXs7s6YGMP&q8xZ%vCyueuUA5@YaZj%0cSvkBx@Q-ck<$=U>{1Nm`40yc7{dd z5mgXC%l=Sx-4l_Q4p*f=lzMt8n9r@6T@0oBPNI(^=XuqJ*^yUees!e?BiZPD8vxBX zeHU>_kdNcw->IAPL|tBeo7i4!HHq$rJPDXp*DTq{yzXw$k#;g%4wE;~Z|WTq=XyAL zhahw6yW1%roYQCa-A}a zvSb!RFngR|NM=Q{PFl5<5>+Yb_nja2({9SJu#?YPe>DXfX7Qac_~T2^D;^D-(^;`3 zW&e-!WMV}-f3i;EBlibAhN-Lp%g~I??F7vp zna1rbJ||hTyAlA7sE&d3tT9zcKwuYKa7z7VpF1jl&{9=5%!$8`$qA^j{%O6Uta(T0dZ$a8kr-&$3*+^@qg>qS++qdg zKA|&P>*dHCC zixC%gBXAh1@$R_2yStoTUS?)guZ-MJLrJkB67@01i?*=uP-8^itiu))6Dt*g?jIf| zY!4@!@rex>@7Uf?W;?8+e!b`^s@-bvo}r>|)%^tLcESd4m7vSVSyE;mFuy@>GmsN6%17-xyrl`)UT1ByiyePn=VzYwO!_) zZ*hsR?vV~KuX@6}ZxO~m8eKQjvy81R2t}v=GZ8lfIB9B<$lp_nW; z&Z^^;eawv^%5&`!2i~mg>py$zM^ft?H-2MSh?3fNZsIf_w?w5ju9XADkFMPw$NedK zkL^VD{N@s=d?3? zUvgJ?q0VP}A)%@C;}Ts8=IKy>@lm^tB?69J0HViH!)$#=)2eUllF#+>f~4U0@l`XD zuEF}?2l-4v`Xc!(wc;OsCkqW&oB9X!#bWoD)tX2I|wfKG!C(>%C)+ zx?{LKWHq(nI78rIq!}`@XWP_z)EITtmtyWn$cGW^NI1l|d~#h<+TIi_iiClDRo6b$ zwW4a*e(B>>aSin`KHKR19+!zY8N{|)$B%n>mxcFrVmjrzs;Vk^W5e*JeElaoJ=RM3 zWIhxWOWyTq9hu3;U>NZS%2i9jsa9We=JK z*K6$`IFH`*`K{+_iwR^TCcjNBE^@hSn7BC2`6wZi~LrdO^W zc25TKI2`ZK_6hKA{lxMpVce+kqPrSKgG&u%P>_G}IoEb|X9xgv<*=u4BCRrLKz1r?%2&W7?zb(i-tG3nTTQaGSW-Vfi^BNubN~b#D?M;tYzP5BYN1JUl)Av8F!I zn!<*de<^zZ|2Sl2MF2XW;|PN4wSTkX<3`ba12i%H#_ffzq$wpX*bPOil%g8j{!$k{ zbw{B^20>6CeXZ5Z>Tl3+KW`l`Q(IV@kA*M4w+*(%#UBP|q40XKh(bR~ef$lG1T4;0 zJ@a#OiwT@mR2(OnPk(-=sijrWP@$UVjYk+RabfrCAb_GDKO6hhrN~z{g_4_lhNvVn z9a5#+&R}A)jX8n${q63nFsB>%z5gCY(e>H`)lW%x_Y?{InDFqi3&ESn^fN|=()|4V zkG88F70*ulut{GF2xz^xSxL5FUu`q<5D&rEWth5{snC1NVJd?gK6ETfB$J0h3in{? zzL_in7sqwaRA_P8@z|{>@%r7jG?0w@j*(LkjP{=dja8MEX>~jKqEB(W@Z{uFDMpHP zTm!Z@M^dihjuMOin00k?v+%9bZl)$;(T89S!WRX%1$0foB1_8F1ZDZ@Ew zGYf!9g4nZoC&?rlC(%Wx zHWiZC=1bfPf0^CPcnmYdp@`-J&XIQBWJZ@pnZS;lm?gWJ{X^N55_8pm32Xnyvl&?l zTyLy*&D-ivkNZ>*h=q?P{?IHl46TKKTrm>~F9e#3({zVde)43^=WxzpzR9lh16VqV zDQ;>?MM_2{K{}a*iUE$1%EuwSGeRE=htGRH!?CkFi46_)L;z!lZwRU8?0q7d>Odar zEZ)sT&Ok;7k7OWHdbXHmwUv<|BQW@$t|4~8Fz>1 zs{34fiRby6h=Eg~x{6AEHf+hm3BQu!rG2iqKmg{C(T6t$CXNqqKKd3n`O@bGzQBT- zTB}(WuGc@^PC8ywi#S0>#&-{OFI<#}FjbJhSC*6n7>K6sB*L;n;cV=JvWR5-J$7te zcP>aPw#uWV`0QrB14-ld{-}tYsB`UJO~2-zaJ_WN3_~l%+ud3BK~KP2dHYmPN2j6> zO|Tg2(L4Op5fgG|Pp#qdCu!;Uq$I-oj+_p~oD2M>%8$Xw6KMEya~ah<2WT-MIsXG* z^cCgwr8aG;^zYo3My9@xXE8ow1^Cx#xv(ExjPg0q^K(p;D~sB$;xoI>c2a-e%AHY; zr{=b{0o*Q_24hTtCjAf#YwKWxU$P}1_-aO!6(vn`+P?4ru=liW(6aDZw3&X-?!k#N z6fx=fWsU}76iwkTe=1}MN?35NOqh2ea$+JuDVKpr;6v&F&*IaAXCgm14JmhVnM;U= z!(tyUBAxSjR6h?h6qL7pHq6%H-QVnuAmZu-EF&?5?YVy8m+N2pD$fF3U*HBs?xvXc z3nEz53fCj86wMFL<0>J1G?U ztqa!U?g1C0`HqwiIeRI(`nU^}25a-AL;K}Xu-EW)PwwYqI-#czLew^TTgoKDK>B`3 zAqE%d#CmeMOm=e&X=+QO+t@cSG$K5a_YO2G?RM?qHl0XQ47h`#Kzg#qw!G}l02P7&aQ8YHOw3?^rw9DcgpGf6 ztp9phv-ksUzX2$re!36zhi_5zhZ;T@1QlZD0@k=+IeR|;npGG{>s=}G^Ef!Z_6d9p zGMpV^t9~W!?kw8U-(wCZC%Wn4fmC&5|MbqTxuS0Ude^8J11ws}*WApLRw_CbkT zu=D9<&-rOF5?%n*=aTfYxw2B10g)+7#P9htIK4)#`&aAv7ICUWcmIx%kPyf7Df+H9 zddyMzAN=AmwY8Wb`R(;DLoy}3v`C}XN|38p$Hvebr@syfq0F+?dTXX3W8+m^lh2!_=XIC`H?u@3t1(sb+g&)jT>LnW^91kb4#61xmGv@aH zZ6SPMX;tv_l#j|SjqKS3txP6rcACAfrDg5wH*atCRnhdxgP*^r&s-M7-!er*iW( z-5ynXmL)3m{(L4$4W*^dKtJ~T_wq4!j7Xf9shJ4DAT$NgJA~y_TUU?_%0-nwvs1w27;@A1I%ZqX1@H~JAz$6IWw~78{Cgdz3Qeve0f(^N$-hEGB zs*a?*(*aA%$$f8SEBonj&^UuUleN41a+(pR58L7ADsl>ELXw)ffRPVhkf-PUg6e#& zNkq9`r`&KZ8ki6dk7k_BaV_jR*8^cu;}c!qTlj29TWGfS4(JD#jp=@SG5BcFEnDx+ zf?;tI=>h)mYhhu+9ZdS@5|nd(>ol%?-dn?Uc%0C>QQZI@K$S(_rZN{1GiMCaIbt7M z3_`Xsuht8_Nhcjm2*sij{6+JIlUixb*Pmt~Rp$v}8?MCN8kG;Pr*UzsII6?NwaXAf zOn`9y;3`>Eaa|!X$r~zzcQ0O~(si$&eZsPUK708d9s9YU=<2M>yAt_T%<_I2;*GP0PtuRn|7rwub~_LE1l$esTmXK(!#^&5Wu4h-EX-4fDB zcS=YLN|%77G?D{DgVG&CBdLUdGz=i1(k&<*L)TEl3~=VNo>Oa`U!L>*2i~*R{BU3Q zy|35ao6s~Kac3UTzhe{IQJCquv2$g*I+TKWrytyRX1sjpD}6(kU0KkPb||*i#U9Z6 zOC;47oOak%^?Oh|6#cvw^4>6Au+GKIz=nnFs))%=-@JkFezgC&I@e2u=IW%*=Xl$tsXrhGDWH?@nypq=Lybi8W|M9#`aKsE~K+pd^U^&dyIbj z_v`;hQU2dLngaXLFYFXd^i>0UQKhTr*_o)xe}_^8TwclC>?yz_5qC3zBigE=>DW zq7v0A{nK{DBWD&pSN0r8I1bf1=Hd98HB~>hk~kSPhuB`{iq&>dT8({Wh&YQiT7!Im z;(bbNp*6kXe)U+a`nu(5xH3T^?gY!%;_Eibxj^Fw3rWvk*lPRljE^TKJ}8}ia=c&m zC%}k{j^(-h2d>_-vx{#ew#}OEpaUU)BxHaqQnbUuBGg;C|3Kp6_hNDg(Ats;O=EGH zVNX!wc+>qCEK9|J6Go+D4Gh*|BOaM&&kQ%5zCRWfdB~@nh2kh7!;x*_IfHSe9Kp9L zX5G$BUrd`UU)eEwovkZ(5dV?Jg-SIvc%s>}d;=+rN6x@xel4k|bpyoiCnO-)9{=k3(#SW1Q9hVzdbVox#7fqY7aVvkezb*WXxZ(h9QNl?UMx9F z+IM%am6|$>0^b)y-Ajn)=Lmcv=V!I%aX~N{aq1MJi6v$91{t23xKH=o$s@uTMd&FhF*DC15p;C50)2B}@v8bYc%i>Tu7$qCqial8j{M8s!LrQCQ89twKQ-n8?e3o zYu4O9;5c-2p6w-a-CFHiU(&^L+iA!?HagMaN(>(dFDaftzdZg>(!;sO>bv{11C^?S zG1M`-b(&+mIoYz^i~JpFTuT^5=&8!mn$klCU}`dotNGmy?_3x@VzujrLDv&~(X1_n zCnlH+!6u$f#a}y37DXi^TLqgQ+6F30$+@t^KE`AG>jCU&%J-p=CKvlv;nSUY$xf4# zlR2Rk+Ks*B$90{Zdj1m=6VsQZjJFsr#c6Ag4*}m`J@Uc=?ZCi3!1W=}IoZ9n2Fg&Nb+ zRo7)dAnt^R)hVjc@C0}2#e{^Y48>-NBKv5-Vj%GDh=rrB} zE>&$`lB>Z}dC-{q5LuF-AMaKeW=Xvk;F5TPrEVs%dgml^a5qrDs-RHb8=H%8!byg4 zkj`*Pgut6VUa1crP0Dy5ZLav(|R)5?Ja7 zyS3oAHh^0pt0p6Sl0srco6AXra;J3Xh0VQwW0>7haQ6Ds?$)A!hv*B5b64SK&uAfv z&z_sJD~kRnV%*%h&=;JavPEw$wUIOhFkmc%^m22&3n(8w@M~X{)#v)G*(a)d1%JSx zxv?+$8`sLY8eJm}2ExkwSa#%3Ccp52QtZ7RCR-hjJN}19?SGPdDO!A85JP`pJ%|@= zME$XWvNN~{+(2n8>pZd z-_}<48|kGzb&!tLS@>xL-NZYmxV1A%o^p9o3=*5n7qpnDbP0LIhC8=?g)YoVBG@1_ za*?+MD|MaSc83G-3*ViSi<8H>kBIzW5M!&^(|>sfJb>e_$Qe(e$BMe#7xlTDb1?%# zgVDL5^dj;K3d+eCM2Z;B;nKEtD(gvr4F+=T9!ys0$57<)%Ej^_!_Ejx$}hCANQBvigQw0V5#cKgq0m>g}wTBc^OdPnT?M6`q|cg11k<^t21dphk)soPr+@) z0v`}u(D&I(G&Z57!S2f~T9Z8>WH#n{VyE70)c5Zn=0Dz4afv-+{R6r^Baa!v_k#^& zMkqnejH76F-~;6UGt}w#vE0d$Dlg z(%Uib{HVSVQ_ltB6>R|(*`(5TuI?Z1XBL)|LnktL)Y8K7fC(?1y-y!N;vwtJO(aaJ zOyzb^bfO|gGngt#F8pd)oR)%d`rA9EedWBFlE@tj8X9gqQ3*!vO0AnB1hZm~KQjOu$> z>>F6OO`z8?SUWm$pNkn4Zf>u=d1pO4J6me^F%-GdwwxS>{JZ8{(BX4cOGQnM0iAqh zAJN$6IKD$CV2Ia1napcSSr174vBx++HBFkntvYVJ76E)>v={1`4{Em#mxcp-?aUHd=!PT@GLX>f5>Q_9!d=ya7cBrw)jt-xxnsq>6ETCztzr z|4;Yn z{PkYi;XmRkdgqj4Fb^nV-zTc?fiX~Mt$u#GTuXj(S(aimXy)BRQ%h)esKO@qdk}PQQ-71+A}RJj15y^M&bQ_qhRh zbL0X6;Csk5$0Mq1?HTR(o0Wv>)2ys{g4E(+I7w+A+ihV%0bi>>u`m|T3#^O7_;hCC zb`V3^->UF-DL?!UN9+<#?aArx>*g<;;1-gzg34&!S@KgRVE za2s`x@xR!-Fh&ONX(rJKm_2n{{2E)M1T551bdZ+}Nb%cmS>;CfBxuOni-jmqjwTM{TeP{)^mgOi(UhM|?Sa8^r&@(BDrv=v z7R0OrgB){m8~kEV|Mfj_yj(?|x-OxMopIj*n_Qn8;+EqMAV^bJftHhZm#akqJ8+=~ z!N>FP2fUSl-^?gkdPf#=x@RFbVH|59q|I=vg z@vpnag%_geNN{0UcNMWS&=l_|BFbl^*Dtn3axg=bYuZRu103}S>qtfO(&`sA{z-jKYLPlV-Ck;Hzn+{G%LRBa=AK-e8a^*fNAxi}VqJ<3Pd_U)*Cjn& zM1!JgJgu?Cj3Qqc2-Db%SGp^0XAUyx{yocUJ8+137h$x0zONubC9&$qg_OIdnJe9Bt`>|tJxU`We=THs9hfH`_VOpM z)Zq!c7JayW+JdSFxgG6JGAm6$SO`n;QUNY3q~$>E;}0tunex$iqQpQU5ufpi4h)#= zTr_-zW(8MQEq27p&FcYfFh3JIPn}C7wPqI8<*RWiUm7S$l_*VQGR~YjnMFu?x>i1R z5UWLZD~mLk!0Mr6dpz;`x4e%)zVYjJEn6Is#h4iB!{23qoF@9OZe&R>s*aEAc7DiL z^jxj8&JuY1pB=s^_WzX2AvB{T^zG!YY5}HV9wrVh5!K)S$+l~B9jzeC~&=!*7)>A;Z1M`{%zui z$MZ_Xh1N;T`;@CA{ky_~k0HoADrV!{!I)Egk_-^H+G)h4PTbW@L^~jt=h-Hi+n46( z+rpp84obQCDRIyeUS)a!b^}ABG(%?rja2dUeH#@24Vm{_% zH8dKXt z`0ASroB?n=ib0ygdxXBGPRT_Ct6#knJ4dr5JSfyjQ2>C=bHRBhI*KwQoeILmGatLR zpNJZFUhhLzt+75w${-F7J-6whek!|sfI!MI*eSPOkpWK_w=Hbw)02I9UOqmtfk4H( zjGI-E2}h=^R~R?+oHyntA^ARJYT#r=R-7VK)jnf43PD0js@fY+{j7hj@Af4!@n-EW zkX8|$A9U(EcjKXEQ%rCcdY(R%5BuBZ37o3W_y9%Z<;{wh$>;(eE|%7)QXlrVZ1^Dh zr=$%Hd;-tX3M4%+vy-u>6pfVOuascX*V>~m9gmk^Pq|2` zSWDyze=x)KX1P9P`dCnGzT5W%LjZTQV$|82~_ z(NSK5htC9?0f4a8PRC}~OKk8Z?hvOdPNVDm%l-ZR*Q&9ny-3`HAyp-0G(5+J(AmYs zuo^OqBc>hy>C+2^wYlf7yy%M!F56JYQyUyJGs~5`1Y;U}2fF(DDc6s)qR$9>F?`5icv}9s$dq!2h;<)APa&>1|)y6 zXnW#FUGC_822}=M+d%Q`sEZCkxim`QnVh8$F7Yj^k;n_?HqVt;U(tevwgNOrOy%}+ zc5?fkbTva?;XyMYAXzmWF=}I1arQa%a=Ky1ge9~Dyag!h2#1d=ZtqQRUC>{&`|J?V z0{;DI&H+pmKQpS*d$H2+#usN{y1H7^40A9Rz0ch~T7tV4rDbM*LBkDG*hbMmeyGLV z!N&3geod8E?=iOsW`Fxe5kuiZ(g-+SJ>T*gejOta2fsY=oQ#a?igJg>?f+r~9~`hI(s$afPUG|R0YU1xcOR#Et?{G2+fB(+wHrJ0lz$mvVaey?PgdbL6g-f1Jqqbo(CuS{R-|I`RtBd4;j; zD#@t`r)seLwTC7y=VJ!YEA6Jndem;cS{tOV1?ZOAskdr;U+TsEm@~Ac}_j z=b!j>12y|dHBxU*7S=;G(Mkf`T{Zjr|BT@fdi9@@fX)jM`bMzaqZ?7&_uqL;0ZFMH z*wY66@uDYjPB?qDUJ3H}!!k8q4K8RzWEg6UAnBfnlhOnU_tG?4+5wzW4xKT^KsJZH zK^GP$#VJ^{OySwX1y^=3d6F@c^$q;ezuazvj}CM|tVr)ffa3GV2n5q%5oOT#-rlf% zc2oQ;e5?=`Y2hAR81REH8yWZSQlLR#&@i(Lwp7rW8PyqleSmzLt_Sv^B*Z=Ejph>R z9+IE{!eZpzALcP|O@tLlqP&U@KH|H)*2(%S80<)u%L8ekW;m{BY&0le5tWfH{~(09 z*GmCzQ$JHD+{5Uen+5*VmPE$!MvupD&c|z70fj?XI>qA`dNH8BBH%To4;|TqIC#^d z%_9rVW@yQ>2T-1HF|Lx>e%Kg5q-SI#?QJRY$`#`C$N`naH=HCcI5bjz%d15^ZfB%8 zj4z8{T$zN3yLRKka&mI`AmRHn*fAw-?n`CwFyFubO5id}1U59F5iqYd97eF2TAP`D zM)dmZ)DF0Mvekxu&?8}w11{h|!p}EffR{FI!x+GyFc8Qbn=Y@1F2K-M1=-^3H^wiW zOsZ6F62&G-`vVWARu__yj+o2V&t8E06p^Dag2V9J=?1b5Q0Fyg;+%#66_%p^%U7>n zy*`b2U~^Hc_@EvWZt#jI6%=!{e4o9VQ|xBaFsQa62R;uQM-IdgIt=~(JVWsQ5a2w! z2cBMsu@kJP4}HS$XUV!pXY6T)eRBWXYb8Tx#ym0@mt;;d=SEWEn@=z|KM%C^j0-yk zw2s;;8E{B1Nz=c#Mw)=R=d#unMnK{Q$~|^L0@f``#d*WHVl?f(uw=iM0O%LH3>~1- z!@P-&3kb1(FYlT;HNEr|c7Jvs2E84RXMIfOxA<;coOZ+AuHOjpe@X=w z{HdA+tapW@fICx3ZaLcgL?j1?bjrOJ$os_o{df{ZeR$}}zZaRHoPj+NbTwp?qI)P_ z5{q*tB8g!^%h-?TWaZg^$X|wVC>}9!#=fx8x_r%Fh%`3OEcIP?C`uKVw7-4hwEgCJ z5Ueo3cEU?LnMIL}PFReGhi6J8ULE5LjXSRF2lt$tGt^obvJiFLP<$u~y4`5-3oR=h za|%Z==n(b(2mr$|&YR6B=>N5ODlYG6Ej8Np@LTk%&GIStHox12-rD95Ac%>H@5ZKu zX>AKjI)-D=sG#7o{!5>4YM2uZ4Niy;n!ICAX1uMGANaLY#BM`wSe$!=ltK2=Azr;W zJ3ltg6B=+Jr|VS11yE@JAd(EhAnfB6Gz8;mpOdDfB2J zt;H#@pWX9i8p0Chk(Nk5tx?^q*pJ#5jThf8Z{~g}>GwqAl4_dborkoHmV`OS;+-i& z>e|s+`cuj%87#lz5vXlD5n$$&_+v)NPcf~$4r2-@{`9l|?#Hj? zt{fs#j~ywK`uS^$`@56A$z#amtOwHp9fxFvH_O+^ZQiplUSYQKc4~plb{iWfGDDu< zN8sIi6XpGg1Mf2b35!Rfa0SxU0Dg3=QhGr4tgbYngcIm;Z#nSW!PZvEV;T-ac^27- z>j0uigb4NqBCGdim8~WY4s=4{wkp7*Gfx%Pq~)k+Be2gGPo166xOY6&x?hMBA$?x3YR19pZMR3y!ij< z!0Xhn(e5BBNI#;%`MA}+0J9*~hSR8=k-;A!1?&EBMesq`fCSKxWVSUPi zTxg7Gg>6GHHVUFP602pYg2gVRfvKXJ>6G%ER$sm7vvn7AzVvK(Oy7a zR5>2~`$rleb;Fr^fH93iuXKd$tiy^-vO=<5T^CW$sdD+HilH+7Hj&diJJbZ9JpVLe zOdoBvB%1&Uzk8yV;|0Kk2%}1XuLw$$v^3s0sOc& z3|3EIs$Oa@_4G7sOu(SMuu&g0rhK&X_)!H3Ck?-k&{6(zzKKg(fnh6oTAZGa=^T=* z@)*5Yg9a($#>R>gs&DGVc@b6N4@6!MM2K-u1@YX z2|xD79@l2heD>4&&70a-+>q4}Tt~8oMUi~e)nPKelDzF*7GKWj;Z!%ljpL=S0!_+% zyqVC^4g|Hm{~HSn)$R{m?G1BT=bEy@3EREQ_$ivS5bs3N#0%Se z3^5pd&1b^SVg=aJ-a&$p2G+U+<&?{?Dh6MI1@ZxeMlxd-Gb&_uSJL*Wuzq=ObFrs~ z2L@5N1qq9E8JRpW>)X^Db^tOOX%;pYO0EMmH&{f!!KxgK(V-CPxL8M{e7|>mHXMJA zj>c>Z3W`H`gc^*uV%eepwAxLWi)zRnbu;48-Bkk)oBqe!1^M}(&t^EKL3fGtj8mbJ z>X^8s%yZRnw4^+BXH7U8WzlAv>$Mfzjb5(CocBH4F(CfClbf(~a~FrA(q5%7x+y%O zv=IlRJ(`Eanfr(7xv8wv@%f0wzA!fA4NGca*QyHB5-XZ7nSEYa`lH2V|5Z}(9KarD zV!Z6!GITdF(N8+$8uG6LgbQa^bJke8Vl$UJbm#Vqhu-$S9eSSk@y-&D*x3*26CV{D z4TsI*^^0o;e_jt=6vI5BX77=oiTJ*3?q?!nw>=%xNsnDn^K#lR7=%atRr+Nvh zG_zM=IiCIQw74Y1;@N*dMqj6v}8&_+)GO-C0Jf>-Nh@kefvQ zU@tixkAnP4niT0Dy_rq)6RJxf8xgr~v)(?a3aZ2}l=Z1UqGM@?t9d3fKG{`c4xeHC zFcu^Yi>Cy$jbuESn(J&o*nfWCr(gdccGOyhcG=}lhAA>`1=9`>rA_DKdg&>GC)8=X z#q6xhZv6g6)3-RwU>rWB;hk^ge>hx}V5r~{#>e-b2*GU0P_kVE!V5{f1EUHO@1%-8 zoL3+NV?9rXfV^vZ45S{tPdWabdpi^cdh_0QpM3e268Nl?D|dB7Tp@I! zidnx4K5nJH>Hrba<|+>loPq1&K=dgEu9<3acY{^;6qj)$D{5P9JhEGUo)c3094cqX zxu0fDSa;jR7|x;-S3iqZ!AnB#DScl>UY)6B)jVr@F3b1g%{KRFY|O9c9M2eiO;Nkw znPTAgMk?aE$Eslvm(5f{Se!GsM$kJYf)l1rNb^zt&@f1+K?R zvlSJiaKmIBr-cSbubG=KqYE4;?RrAh62@^7tI4zcfqPPHjye47kjKVeqlsdHoRENm ziZ)r}yO5?Uzj$)hCPMMEEvz6MxhdSe5?%K4I0tiehy#r4Hw00Wp8jOKrzuP+{2N~b z?k?%y^`l4J{sBQXY_5_L@~SkmgXekc)20k%g=_nG!pkR4+zp9T+9ich_6XG8p7S`j zvigIpazE5LJafX%>kp;ckbK12w89~ew2_+ujK{Y zX_O}Xkpg@#g4oW`_gejc9#5?-Qs|985exbk6nWw zP#jYV7}vsc7scFgDh5@8wmie(BEy}bv@?E49bLRw4GoPN>3;NH@^fviIdTpHanGgx zVgyAgKEhL;`@YLYJ={E2Oz$l97>Hs{W8I@4PJ5~h zx9Msg(CCK`!`&CFHoegc^s@K2H1wZmZ^sK_jE_{8;U(r0<$4b999`sD{njF$7XuCC zImM}`Zo^`>&4xcc5s3XY;5|+_(&M1!y!Oqp*;<2`1rYpLwHC(iw)mx=u}rnR^>n3+ z#=lIfIBk~*)u_Sxdk>}^+qVuKi?u21{5e`5sy*+g28|E%c*d<%pu*R=v z&F6Plc5I7}SYJ|4qF}64pCRgy-X8xX~b+ zD^vp7t0*L-pDk!s%SB>ydT;(jz%u^l&FP?N8Qo;4wyP@>kAmj{zkp)C4C-u<^c%kI zy$1N~bORx1-TCFNapIAF80!l#9Ql{#;`H&n_(BBz8>o!{u$NeoaLN6tZAGKLkB{$D z8jXjerRuG`3!%pgA;9G$?<30Qp~Ufb@aSDZK|vi^*lX$wjK*~Tv|*rjjzAjStmtK) z=fCy@C*7&*g8cMa$ilPScW0sBwesIEDit-@bk%RkA98P?u%qE1V3llDNc2J26S04X zY;oMXqGR48Hp%cWs`|$tqB_fg;ZC>^Df7?FSKW6$rO~CHpKwZE&dIBASBsq*Zq7x( z{JR6V12k1Qk~h02BJ}ndkFtNfSV|XG)b6x+>7A)*;+y8z_C(vkIYlk{TEgP2{toErv#UZ9~PpoWR#2H7siMbwBpZnTlA<#~ugPseC zTBlF<#QFx0?UzV$`mJ@%@PFq+~s9nA2ueynqp&PAqa+nJ%g+Zn-~+c;BLb(0lp@}HTdMrR&Ucu^H$gL{h(PEK$hqWiA3A8l#Ckz5H0ESR~) zPvJDWY2t@fqnBe+xMQ&4*8#IlCfuO~SLeRmtmARuGjof-R1y_oSPSxjG1H}Js(^af zP;(5~j>-EY(&sZF3->7JD=4L#AAc?=$P`zGw}B*bRcL8T1Kimng6V@ zZfk6iBDP$d;!MwD2TFf84$cA+#AT%QLMxJAym~#1K42~0eYxVJ9VQ3;3~6Iti4Pn~ z`We01j-Lm9tLD(IRw~dauU3MBXq*-GkCQ%3jsO0qndH_{n#T}qrjDh{&Teoj7Fhwj zAWd7HO*(pT+V+of4*

    %r}WY}fpDEI!&PLX73H+Wi?U9d6Sbnn%x&nVHG<@qM|fm+v~vPR#X|CNHKnJlFNxe5HZ(07LARgVhz@6h@6XPpjYJ6(OYxnCpA}{A z>(2d6PsdHi_fmZSGJkH7eN$xE3o~YVT4cGtQcrhK)Mhv^%;xR%hrCIm3Tg`avg2>v zWtQV2Gq0+uYoNffD|X>zUEI0J=j9@C;j7z;h(TZ&C0G95Nb$h-03sf0ZNmq8opo_W zvi$VGiE5YVyxofDK=il>XB{ubw2ENDmt^`c{S+nb6uy#ZBpe*R1qOxBlAGMKl|P>|SVD;cOD-Bo z93PS9^v4_L<@cGVH=AfW|7M5Yb!cM4&049>6fmA$t5dIEopyfxq3Rnf88fD;TK@{CHd7Le}2@I?XX3`t4WD3UAIm&3mIv& zv(j(4H{`A-=OT9&=I6_&-{&b_uQ4`oOe0~W=&BpVNzUYP+%q5@RcoID8|rg);h3ipO=c9}lZ zyqZeHhbPVqTTA+QPJ)j3?4)yPfGaK=1jCOo?ZpJiU+$AKCTB#*F>P<+@-MxYJ~az z8?L}bj-qvz(TE%RJ^2O)xz%VU{$!u+)o1k!BTFFSU{Ku@jJW>_=wNuNpylvJ`QsN; z;%fI0iLwdy0pb2oF9Ra;+Lt)sd21X#4AIqgZ|!mfg)xT#-ACdt{Zj3D{{1NIZ|AK^ z=#%q&3@~KHy0Q_$n43KPIXBOuVHNQz_@L_Ao5!LZ-RD1#a$gf$mR zoz~UWDMRJMYCBnd7+s6c$R9@z<%jO*ly`UQ5M$o{MQeoA{6ZK+*5>8}f4VF;yuS}h zf&WEcxiG4dWe7{46NvJsJkPp$)k>U@#gZ(B>qf5tZ$IqaeEunkEDi!>)(EgURd%@V zzq)*8WHn2o!FN16mC|Ah;m37f!)d0;rxflvI?AJWdB6mFN3QU_;Wr^hlveii$omkM zT;=(~E`wsHesOYfsnFmuI?Bvtyu+h!%DYb^{4R1Jr|h%aR{8wNaPI-V-m+K|X5KOFk39g}ZDPevB(BrQ5{ ze|5Mx$8U?CoAj;nXM;^8Ow%WIwY59L;|zCBEW2{nt24GFN=9^%=b<d7k~3E!1; zU|!Cm(JF%T#LZuDQIWKtpxhWKd%gW!GSCQAD&1yD7ei04l6g{tqv>#zQi*8G;Rz0OWz|idOR`pAYJ>F(FZej)lRbM z7Rtq|bzVkI6PSeHYl-;pCEf-YcH}<}NC|++ES2K0>miH#5ih08K^c$`h} zsyv2MgGd&!zV2;R*+T65@{__8aJOiYPu7-~Lw2F=?T68S15SPyb_Lkg8r4piNH+`De zmY$yB@C(fjF)sqy#Qt1tASP&y&0h(Q2PQ{HOC57a*Hh3t4b<*8c61wM*Ukh!X;idx z3pSSuZ~1c06k%@+_9xK&(Vc?)GS|U`gC9r2l;#T)`n@dfCc-Q<-?2!RjFF$(L1=ZL z>ie|5!_}6+$jpo*K!l~-Zv6Uvf#c3cPFIDI^Jx>^mCj!h{Sx&Cio2Bj+;5x(7!pTU zv6VfrSzJ0xcDCy6K;pqZu`I_Lw~5hkX-T_2rLUNa2a0x0ia8cNJW^|QxF=JxU)BhZ zLbzRc&n;U*b|{IRxgR+-ob$&>p=EUzbVJwjBg zLOz~$lCLT1-xp)S6!&{q|Lpf8j<{=vSnq~0#-5$dfB4l_<+`(-6x?An&97Ssq+q5a z3{*4@w&l^HoMRg!F3gF#r@;TCAV>fbtE$0zKEtZH{5G7gP(hS`&P**9ys9Ha0I z#}6{A-Y?eQvkC45t{B)f96q1$q-+}AZr2v2zusVE>A7*yuPVP;o!@_dk0XD;w9dDv zu@-VJLr>jase!K3Vzkf*sY3V#gYIY{50_Ul3 zIiUzJO#EP%4eCgXr1XjHmyMxjn_DC*zy497ZzmoDJ8i$dq0tyGCwC$Dywmut#4bJ(MZ8yp_oF zDVk5ZpS>wCrPh{EpLFj7SH09kbhUw+)XRKHx&jnb3w-;c1CGBg9F}^0E4L?nHbQBp zZu${p3>T}!2Bp|!|9Z+od}qdG81vLXG-T@7My^=}>aJIf< zGBPD6kLEZ(@&?24qC{1WH#ii3=h%dO1#H%4)d7ZH?5l@2xYH&)XH!_=lj85!}zmMKZSZj1G=8?(R^rz3Z&_jwYE=fd{3 z3UpQ(%jE8?j9RR5FiIKwOlx)4r(^vvu02fwy!~Vn7^NvEgr(xjC<#MzXG=euiR10{ z_0^uJFuRE43k_!n9jLF=ZKykGPmi8Vjj;Mp&Ek(dC23&TAY(iAB9=W|489oSoW?X& z)6qc;E(4C>L|lG+kvBA?j&YzYUGjxi9zFibf=XjBCG9vAqy88lD+{452Ia?ppJmiT zqt`>*7_K9XZ?z>Q5Y2@-z;%JxApCy80nF&ED$Lz?4)ZmCP?NpY)FAx7^nWdVp09%d zf0-QyA3ol{c?LMY_(pJSGpI7yVw0Cw`1Kx>*oIcQhoU5OYf$~TAdqz6n9*Kp*TF$& zF+2be>=7w~s|$1{YNj<@YyS2Pz}@0n5rlb{xcSnT`dTtbm?2LA)ERkvRDNrxSb1DT zzBgUm%mfcR(iVU9TIg(1)?ky4H-3PTXXLvkRX+;=so!< z3t|!fTH!`{qK~Z~u zG&ul?O-F-+mP%Z)i2s{G-9Vwx;T>fKqZa(gM>_{&lhvcz6$IpFE#VeFDJ>23nPQQ1@Pmlar5SDFT{ zImWLo)AD~G0%~vyInv3vXFh8^A(>$N^DJ6d#0Za6L!fUF z_v`2XtzG9iB^O9~u4MlOx7tr9|55Y~Q_{^AwD@=)oHo&guVNFqF+N_<_LT8n%HOH) zhXU9Oy_e_^#@Cv;==3TzNHVflWiK@*{YPCL zDYYfE9%`i_>!nZ(cNBHY{b`G2Uk?S1Z}pxk=M0oC)V%Om?05Yv?&woVPMHRC3i;~! z9_H4sK+0Cd?}aHJDU6a*m+YMITIMJI=690v&@a;S@eK!mvZK2N za-)z?=p=^j$p|*RdDF^4D5Je8=B^bZpED*6CBxUPGPK(l!+p5u1DC7U z!)@y~(f_B3`F|^6;Q!1(L0>S?CIU`pbVQPM655FX9sLRw!k%!7_V<8r<|@aRGeB`R z@FVH|CDPGeeq?aM_YMS5VGuK;62B|YjEqy#4h$P5b*rtftvnlD>u8$$)H0TImZ&yS zJkm3g&+y$!xY#S~g;pHuYI4ZuAh6u;yT(f!eX1GE5|&K>WDGZ=B{P%NtWD62B7I=T zT7(_({NTOuC#o^R941*Ozyz+Gmhhv9mqNdCq{L$V+En~F9bonQ=^w6rz|L(Ap!x%R zSQi_^0?;p@R`K^3>{IP4!cG?82QbFn)~JqQ<-q7iuUKP_@Mw#qya}fF`euoLiaNb> z!&*T{6kv0rR}GF_Wk4q3UZQNUR-oqjr? zQ-T+#eu@o#=_Mv#n8w8fNSOX;fibhVK+)(|qC4;!b{S8_ij-kKLQ92G)OVZPOnVtSheLuzy^MFiJZ*gJ4S^gFDiJ`dDdLI5}WAbav z+&)&L@&E+F-$Q!HQttG8rIr_}PWoFcUkn3Ol#`!d-FLOgOixX&q~Wj2R7PfE50oy; z$C#*^Rxvgvn(=u{2itYPJR8SU8gc$8?1%@3FFnvEWofi`HCggKwlhFSS5nMRteO6E z%Q@L>zwtbF!)Lr5CQg!&tvIvgVZ|IUh$f!Dd{JH}gTrp;Y?3(kwNWHV_zcO)DySas z^teH09^HkAj`K9+regybA+`iW|(nd%YMOdnX8{EyLD|+iX*<1gowvaxXU-!m`^zssv_1dZwpsn3v=#o(jTYDq3^pu<7Lcj?xs2 zu2%Ux4h*=TSKKDIxLuolHdFioF~J8;Zq1UO-!H9cF~rOQ&el|LTaVslz9x3M!1D%{ z2q9^K%V4uUH{C_%&0o^+A2kWt^B@#>ADl#oF%Cwz71}Wj>0h{Xw%RvCrMz$~jxiy9 zZV;m>TU2dtW|5G8y}U@H{v$N2z@B0~AC)G$hEDKQ&08z?<`%#~lq{mgoN;x5zd5$nXJNU^#1SOYF=Nb$1eV2&1f#vGjOL?wC1`wy) za_6QTOGx$^y4C*vh078*E(udAC715s{h4xG)cH0AElHjDozL$0Gc;1L5Dlk#;xCvc zYCpQ${L#+)X`Z;|4l6T0Wbg3~-Z3y=2e%#C;jaaJtT{gysR;d1{nXjO12*E?`Yg_3 zvZuMTDTuz)l@9Cfw&O8c?u4Y`2!yk}De1s%0}gJiGLS@2SH$|`7S|OSCo;-E&!{p< z8_6-x@cL-j{6>;expdA)6;N64VB)qj{E~)HHo1Z}tN9^UIOs=Zor7HA^MHt|fc|Go z$(tXvwcE0Vm!k(M5Y;Y6bm9c(B`;S3KX0?O>W#bQ%Z1bIB7O5b-KW84 z-qQ zQ{9H`{W;c!Uy%E?P1_RhQRl68MGyc^>M8r>TV0y%*ntNC&V@F(WkjeCr{yC|X^L~@ zf>c+>YG65p%GqCe4R>Xt51zToZPQN5dR@h6l!A|_&GzTh<}ZLYY;WbC23Lka0P^@+AV zphQC-;s6<0nx=OkI4y2g%Gbwn3nkn&{PJtaK7=P-l#6`*H|cA&aGI~VDW2itbUSM- ztOccg>Sit#6-MM$r}KkTBX3OSI48~^ub!&>y=o8v!26}TW@|ovjaI@o!!k0_C)nM- zLutOjQBNTEqkZO$NFSm5nMojK!RAg2xj>T){-Ow}J?qY*J8l$!mz!KVtORffJ14uO zP>iZ0m(c>-fAlbP5E_$R5;qer9cf7WTq2Zw2e{%X@N>spp<1T%@~u*spVvPNZS{0|@And7p9NlMP!``^Z<4xoU^@vY?ArI-)Hlk# z3?Xb73%TWk-V0e80pE8KqEAutgNAk0uKYoy`aTO+QERE}_f)c*isW1fN6-A&Bc0+}c z9#rn=nil>JZ*ejB0lh90yv;uq9a}9%4*?|#zBZ7WyYcx@%iVe>WU2{$D6mx65vYFY zM;r87V!37Rw$h7o)3pbQl$Jmgv>gZGfo0F#1TWzIl4?M2L34rX!4ui|VfshaZUBk% zn(gplgBdk|?LZWJ=Ot|U9;Ez@^wnt)qdH%F-6*OFSK`ArR(fDFh?Z7X{3Mv>G;rU) z)X6Su5HRCq;hBmcdNTbd$`+B?ihDJ+;>RGLZMoJx&@flI4C&JNI;^ysN1)f#T7i*o zUq@F+wv|Pk|3$BqI&wL5gwdKgJybtRrvwurEK+!uw#ASh(vaxSdb97?iI5~~N!zHt zy)JLL-Q4^(SJEZ5$+2yqB2g52G|)D|28nxBa5+<6cPx*=l-^sas(!7bogox`HZuje zOLD5Ea=-|6gL!1~boOEWpqUWE#gWE}W~R!(ICM1B&#%bJqTyv^V=QIF{*5VEs>6Kx zrW=(>+7qk9WCr5$l4tsQn)o8Nm*$9;`oel;Jn2l{z3(fe3@ z+ND$SJh_(9Fh7fBA6W@g((NIaZ@rP9RE)2y^BdF+-~p}X&XtD}QZqWxqZt=qFEa3h zWG4y@=1VzRY}hmTitx^cl9?4fQ|F2=uf$Mk4>@VK{u7F_H8zeOW%IDmQl#V`&5@*j z++>!I+#9fc-w-Uj)-6K=m@a>>j^-26*^Go0U@oqnZT168sWCstDNe6q(Ku-9Po>xa zF8GY;oFx0H9Mu9^V#J!-HyC8G$7K?1K*)BI{FQ^*y_&NkzDy9J z;_mZbOV3W$R%U7xC~{1&f2FJtatv*1wqsKdLLQqop*yE}{Pd?=bg|(DPl;7^^5pn$ z+aJx~SHp`-O&(q&`bI|FYDr9KzL^AC%E6Aa_26|SE;`8s1&o_d@5%#^$6afiajrV~?V^%-Ur<#ZtHzTbCbQ!l$lLarDi|P=H`}P z@h_Z>syH#w8-v%F!j;T+BYEErVsSZ0!BqiHe-Ky4%LJ_MBZ}HXLJvzSQqf5aDgGBe z2i1x1Ypk&IJ|-6H=GcMd_KT2bYj<*;C`JLOxi2c)c^Lx~vdjY)W5ifWfrLLHp-cYt z>(^jP5tm-o@et2$i^YS<^pjTHm_#s~wbAZRYh;ls_1c|N=FOL$%rLL0Qs<>crcd0u zC)*>!@6$~*-H%Qx@T2tvy8#>hf6>L2YSZcm@KHvZw0_l3$ixTS1ILp;z_NQ7DU z!Ry@P9`S0zQMX6_*s1^1l9=H^zck&jZOJ5*lj_97fxYjyTrv4^`S|E@VMmD{!RmS| ze%)UdbdUW8U7pEAWE@CS1%u{FCva}9(co17tq-B7HEH@p4@dn;Po@uH!O%}xQD58~ z<|c$+-;l(WWHvQP8cBmT_Yn^=x}e9nO>uV$@I&~A))P$6v8Gq=>}s8ja=3)apPlEI zy$U;Ja^!^E@9ovl6j>Fw{xBf9GveX+YC5@0s{B1qU=J7Cf$p=kRBdC9>ZXUq3&r}Z z-?0wE$DneheG0jmRpUk<@%zjwI~=+Pk&%gxId!uHCR4H+#_fpxUf<K&+8~F>Gi6O6s9jpNkmVe=O)tqC|1WZrX;42iR2<8kt{4{W}rtMN~ zc9wTU@KMU+~B`JT6_o;pke4E$eQn^fob8W`Yk6< zm^jCo{4DMCALwdQ3fYr8jSZkz?c(?<>yW1Vh)9G=PsOxyS?Sx0OlnV+9a9UM_M(;n zl28Ahf1~}kguWJ4D9b&zn!NhEXtNNn_x=~3;!5Dh?5(X{DZqyHTA-@IUGO6iTcsBG zgN(o;qS6SkHJql(jx*qWca4u)gqu5H^@rRXaRKC9{xUyaJZHIafbbw}rCv}`+C4jU zzdn5iz@gu2=(r~?41Mx<&irWR7Y)BN`HR6yqb3s_jKkFiS6Daq3EQh6T2_UXHsc85 zQ3KEqU~eZC%$z#0rBCQLcrvw&Mfu;mQ*@8RXay%hg7Z$iu^T=!n%CQY z_uG3g38&awF`^2!q)+Q+vfey#`VANhQv)JC`zJ-;QGbO_{TuS$@MAPO2mD|p5nN7L zNZ^y#rB6(WfVr4-JVzc=^O{UW`swijOjN&`#fS_IW_|Bd!g7K}{cQ3Q)ii}R-@NHU zF$MKGQ&5EO>20Fw-gsP;sbF4dtK=5hwMASAhY(s}d1OBC6)+=EXjhIB@)1y;KEa)< z`a3NCk;A?Rht|n4Ls5mBbibBS%+g#6wuL-_gx7w{Z6Qq#XnDJ#{OWm)^v zUFe zej|_9%#WmnL$C}|xoZH+^((+G>CA2zL|+;F=)65(`H z4`22H%yT#fN&D_r-yOwu@Om~e?O?vo5(VJtq)2CCQk%8^kP?@5p7!dotKb54%w930 zeXbpj!1(?LcB&vXnw=?=Xw`y~$9|;zzN(q^(4O~)xMty^vfIv1mOf?NqWu`;cp(6geRtW5ilNoO_@ z6p23{ZL7o_me(q8`EpeMdX4RN^b8{|i4ikzigY+*PT<#L|Jg4XlQ{Fi%%vRZ$!3Bg z2^1Z8^~Csi#0zniC=jTaV2Z{aq2bOET@6Fx&*~*4XbYKwD!>m{pNwrTe>@qdKlnk` z@tQ2*BZfQyoK4Uq!yupG#G1&;|DYzymez4_O%;pxJ(@l$8GUt1CUo*vJiO2i``K++ z7=|o-_GgX*sjv}m)oaZ9I(xxAR6$wM{Crle=N{`_3lPVH>*BtbzRLB>^D?!f%i=&e zIvh9Rziz2VQWz&vc@io&zicH_5a0P>WAF8&uXma#H4qwe`)~11e2<^h51m;PzX|N1 z-(+47>~6MnBH3V`yQ?D#$<{HhAFJcd-)}mI>wG&+@yr2W3-r(!)-BPBO$n2nIu+o< zQNz>90eKR2Ih1Q8FbbNqviB!4>a(f0!#7&e5WC@yP6NnT=w_XcmzS3{<9MQ@LFv`= z!2TCLD>C1)2h6cVNrK0&URtwVjkTsn(KicpreuD}&qsPHJfKM}!Kqj93rXLMaJNUt zMeNJ%rPMJoNE6;|oFbjV`uy=*pF!$=p%T;R?>43xVqTqm6fjy^janCjJT2|df1ou6 zZ`RwyI#}L?DYF&-;xrOitw^J^chxY}uZnod^>-XFEF?{DqhXec35Ro`)&jGAzIMtW z2(%>QEE9Wt{PAOBl_>L2=$@&GduVoP;vu3{d_C=^;rEr!u&$I>dsldnXK)war!*6_ z39O43D;npFj*5zMJC-+B|4b=YG7c;Fa&^HK@aEIfI~wSt{*O;ck^k-3(u;MkW>yAr zObZT*Js(eRHur$&u(60X8pqTeN3ZaoL*{ZC3@%|_5FIE30RD)QcO7b)BL26V0~zTa z1lUb-Jd-mjdQC!n3G`R|^2T}PljCvfM!-nSxf%b+Cs`3uLDlq1t<;~=CFbyQKhn%d#`od(MnuY_QJQohOHhxhOC zxvlmldja^YHC>I&ony#>9$W0#7p^N6DU$f@U~4aY)}HpuS(47Xq3XSxNBfhu$MWXd zUt`~(b!ZV%ggPsnh!eCKQD<8D<9IJGYi#(AUlf6AMd)|jx6hcGdd~}}g?)l{zK~ql zBEq23Q;{_7^~>`bZ9?uB#;~(lEdS6~GUPf$KpAJ{>O01MOvKyEKiLws?Q1G5irZa#KW z&eT$=0*8J3+^JU@pPy{{hCLJPIHyoRasWbVtBpXF<{%iE42*c)JlL~KEI)~R_pN`a ziGXQ);wGingwHE?)?aaN72@LlG%+g%U_AUp7vFP_u8yn*2L~~r;e5^x)QkX4nd*#B z(eG*TZL5jJUesgkyUtX6OMoQU4G;9RX@y&cW5Vm)>s%xip8t(H2>^c*Z&E--y+!p` z%vDp$R}#cCjBxD?Z>h8ZF>eEe0dpQ9nL032|Ny7^%rWGYRSN+Q?b zLo%bWPHWH&ESQP!L>-$|OXp|hjV<3wgAXd;feBN{Z~gCu{5o*l7H6vdDEdz?p~R!JKVWZ_%2h$oFI{ekZ1? z5K*E5s*0>>yPm{;jsbC9U7be;~7eB%N3TZ z>p+~u@8tzM5!UwE1DhWOC4+XL=(fLcwpX6rJD=k(`)92?AFJ8~_6G3ox^6=-CKpef%seeEKy!r;!2tpisse(kp=>|plLIeoq3wLHwV!mA9C2H1K@!>TGNiQ zlh~mgB!Jhswh`+8;x!Z)#P7Vq+QBQM+~GRa#d{oIR|a{9w%+gLl$E0!RqGTMyKls) zjPM%I2l;cnLVhEx`*ke@&eG_rCY<@Gw=+}I!X@Xl8GL{qKc$BG>BS#Hj_2W2qbIft z>+dv30i?K|w7}Ef6+|W01A)Oz*=B9Q%EW(mFel$)VhwYZY{Sb*rYvz%(?s#XK{<)f zS`A0NRss=T;v$=Vi{nq}bLhn4L`QHlpZ$Y>Gz@P-&w&eu^)H&1yDn+Q>U7JiT$6Qv z9c+DJ&y5=nze?jQ;HO1f054X2Ckp0MjDc|GuxCBAem8sQxPjqEt^Oo=oHb2sF|owk zOY}Uc6BO$2uDLUoTcj$JwfDTtFm|_g*@3gT<$Nu0loEe+iX{r>LT3x$KNgOXA z50@AN(@Cra{$Z6fr@VQ<%IaXjJtk&@6SNCSoSntXY)}-oVmw4NjJHI0*4Y znu`CurY+%(IFKwMuZ9QAE(%4hgBIf5Qa-sqxB8wy4^s#bNU7VMn-^?Y3*uN!nqcV@ zmir@3Gb(;2z>%w(PrK)^IWJ|_Z*8*C^0(&hjtTt0--)^$$t8F!<>*&8BUvHUQBQ_W z`(_Dqkbr|*JN4h_m^fsl!T)Y=Wqo`zH_1_>k>$YPt2?6wk~c7$7czc_M!%&|@((wkFQw`#$ya^Q*-W9+CRPJ19i5FX+zTz@(2>&}T2;1eKt}JQ2b@ zGU77pbsKfELHm5evSS-kLzivLcUt2mE!)?Ft3pw*G|Alt`>yzy8dcVty;TI7LU!`~ zx9QOTwqL<5|Ib&2e*Cz`_(%ICokf+Mn;7u44_5E|m^g3Qq_P*sr=7{i!UK`wN!63{;-Z2*;HDqR^agE z+du8Oxsl3}w2rb$HG*jbI-SERm1S5ifa6jHse^!+G5g!B3w0asnFF)2Ph` zu$S`Uu~A3IZGY4!etOT}T0ePOG&``IE%imsW_IcA`*tbQyucUw=Oz8<_)n7n-&2F? zk&$#w-;3dSk6O%A9jr;2pGqbEcJae~_b)M{)|J-rI&oh^nn)NxSTv86cR`#ADGkXS zh?2m|8AAw}9rv0X5Nsf(@U?BA9W`%n&T?@73WZra0+S2UX@A&}hDf4nt$GRgghp}3 zVff%RDB9zoG(!0XU9AwZEE#ArIvnQ9p|pf_Rs_QZO;k#55X~`7!0OEf@$wW!58C`# zkL!oR;*w$~&I&XXXKlGGCAqZ$L0`BVF3H z<*EXwk9<5m3p~KFnD%1e?s4mZ!_)rn&PnugSJ2S zt^)bu1^gCJBTW_4ELheg5Ao-I!hS8+6cSPbTv;uXy?wGnetDAtz&V19-v=qJ2g0e^ zkS7ObN=WO+A%800s8HUhbo0kBd!5`FmY6e~8xst2^4;<*uXn_nR9lDlXMf9I;4yJC z|I&1r4=?z{Uq=U!M9wkuG2h%P+yrD?$=6v#e@?DN+P)R~ax=av*9AvhHI=sWH11@+ ziO7C^aan;o85y!E`c9wD?#AY~-tT}vO5`+il6~%hC%2my_)Mx2d24|`Rr4N!A~S3X zUt0hfycX(It0ZK3w5H@-Xo`_eR=Qx;fJGw9D}M_IDkc~Ud5juQrRmpNUTTiGDD{7( zgq=>&Rr;)4z3MjJ@zdt_s{2(zxXV9kW@4&D-$(^mv6oteYGn%>`m3WxXpy13LV7`IRoB?d;}7=UVxE z44HBIzKoLfW`>#Kog6vYgNw;QW$X3EyFc4;zetM}r<3H5C!E)@u?LuvnkclZ@W>GX z=~;ayD9E%ULnqONy|CIk66FVV5cvmO+~Vq21gS=k_wXMylL`cBk;g4^|;=v-fVY06)v z-T~k-HB#nC%Xy{YqaDn`yb2GPPW&@3U+=|+lVtOhZ&`>G`#ir7aBO~uG5ax?vY&ue z<(o1@EHQnao<63NDx#__0J&ecRBb6cnpOTj2*oGKGyw`##T855L`_rmC3q?Ir=HEW z%b0q(?2j5oN$>df;jgsHdsgf@sC8h1LLNieoRrQk^mPZCy?AArG3_ge7p`%$@-%Up}_UK7n%HTt@3)kJ%~@JeJjzKiyj-RY*op?28B8pMXND4 z56?`~j7a9#L`*?-HJ(?oB8(Ak6A0^S0+S50m+3lcWM$iewKf@Yz=8>4cvW`V!<{nz z-))LZuV}U1hzcyp#)fKAgtdrvrZ#`d5h3SsPsW=6+-{Do+u?P<4x|wNDIWGtHgk!r zP2TmycA+(mayvu5x=(iKPW={E1Bm;2@9Bw`b+efItsOvnZJBFtwKqWu)5Gp7^AiM! zG@RC=&;0>88nq4@1o2KAfB`n|t_YQWn^tp4xAWrm&B304j89dqh1K_@@`qh5U4ugB zo!4pUB{iF_9rrx)>$xtiyAFTde+fC2gmxkX_)u*}fFz}wRfiu@3+729)J z5^a&q&G8aPig2VZB`4^lx9=Pg(20`Z{ZL#}Pmd!bOc4sGsr#u~V$1P&=LvSL=Yz`g zOmco7dMTU7K`BYU_BW&E{OKiELa~~oeWyNPRMq7(K`9kvM0R#7dI;rgYfDZLxp4u$}CUS!dTcc$BT*49k zA1tLi{yw-Tj?pA8bITj=jrFNPy>w$ezehEkLvumx-2OD;?S7?vJ8HX(!! zn0yJq0+4~540P_JhW#7pf#DLbC{jO<@KsaTscXk2C(B(O0etLF(_F=Sw)gXt?4oSMB>=eBGv3hN z2$fEY2m}z5->em{`p*3@t&+~HHSnbXptyq&5_&2vDo63lGQBIO;qx@sGv1Gdb|TUt z4`E?qb@pRl-a=#ol%w8&;a7;6UUBor84VvFSepM*aQF zgE~YAZr#+=pU*BoFPu>}x#+er`QI5skj{h+_U zzz|7Oh97D_$t<)Xm?Le5rAgrwF>bER;M`H5#1@`w4`S@n)52^;M5en|pOb8#nDv}y z_bQBy^oZWuF)mPo9%&Pjkn!Sim4v5Y8Rp%K57E=DEY-#|7J*pb-k?9$?vHY@imf>@ ztiRdvKD`Xg5Ghv>Nj88&yd?S_!8YuqxZJZw?|3db2L2EwOJFM)v28@FX3BGn%NcxY^^(^Fmm=a3l3JK>8ud zmv-K9F=&Kheg@~XMQx*Yl0)Xj8y-PeN^{`>1;-~mgO+dKL>0SJ&pD#ewS!XN4%?!S z%I7J;vgrQZU0J#@=U-#O6d>4TExFyh_$;4J(C7KJtnEC21o2>|qb%;3vnsli@ZxLw zlH%`6rD01aV8%}%x16D(qKkM;+k0HYN|V@38D;OFdt$2-n!nJ zf@(GNZCw={V)=`@wlpt8{X9sP^wma{|@|PQUchN`XZ0HityN|#~z!Lr3=ja1|lIhuE40B`?Cc%ZXcPy-4!jO0HWZ>Wu*IQrzOo3 zDhmN|dQM|~)0k#$c(!@SC2{R>URU6jwvE(knFl)^gI=`LT`rlt=yT~PoT+*~C@e7C z-t*v!fBu&vLlz@JE^0MDeKD}yrRjT7(R6Is(6n; z4|tRNQSvcPl6v38)<8T@n|1{kbF|+c?Ul?7*g5Dh7BXzbr2I_*xoWVe=(BJ0A0ePo z`O_1BjI(c$`~vkC@Y4=maWiuiEjxn9+l^^T34@ zog!2-i<1R)?0VPT`4@h8^;tUQHRDdoa*A)TZ|UYJF9T+8dnMZ4JOB%`hU^7lfaTx; zF$T?Kuud6nHnvyiz!3Q6{rs;xhgL&3E5RyR2F!~#!V_P-AjGmBc3(o!?NtwpFzu03 zB|8U)V@|8vW>ofy7-qLUbB*U7MP|RVUfLhDcO1};d!0nXE6dLqC9>yqFN z!RwWVZ5*`Y9e=d$*H~c0MHIk)oka0LWOr!AaF3jT3|$~&?aBR^KMrm z&8Cu*TN<|EKItAm$R>4nqbPZheW$PMuCNUyk#TnRhYMM-pKYYpP)^~FTwo<*459DO zZu*APN{^@QuvmD%2Xd`#Ra_i775NY+8X@!$oU_z6llY>(SOSg zrj{;LlI?Os&I{~d32Y`!5c52KQ#CYhDl2QbgK7?&ybZew3)LiCMe4j*-tyvA#=m_X(*k| z@Az)3Bha8arZX&qPcx$*19F^h-M?Q=L#5#5fu+F3M|R|b0bS*ovg;ezcru`L>DjC` zqNb`|$;$~U{2p_5R$FChdycjnY{Oy5K~n|Lw8e|srm7G1O4Pq%|ASqADt9)YOC zx-qK6M+EFKrXp2hD^9S%`Vj-NU&Y<2jb}Nw0(&xQ|Kg?r_yvl?@Hir|$^%lekms_e z5K*?{5%ah{6*eu)Cc%S^<5P1A0tQC0hu;q{4H8+xlNrzXh_LW}QNC5jkV(%>X7~Ac zjB)3fTn{*)2G*ib89{*OIslg}oP-i5J0826B1r`ex7cn}M6e5RR}d@0lPQY}|H4_> zEE-%Vx|>+@7>LdR(8i@|Eof+2ilzkV$V;e3mW5ign~!@p?Wh&ppWO8 zf*M3?cW7-HH`UqBqU#`QH>wP+zc5K*gj^d}5BTs`N>U4vC}v&z3QT*w+mxoQAM_R~ zXU=>6)DA%z+k0EVWcFgmLE$`Nm(VqRBOg!U>2sr{Tr6K6a<4H=#fykH-la+|t2|wW zcJ{)a-rk%nCC;)iQd@Q!g|vvz1WK&i7)6 z>A!2)1App8#-}`W%jDxeJj*AuGxL#JKm!Xg^mu`DXea!UZ1_a!sr^F!h;{i~A#gzMNCS_Wiu9Y@h2>bTa1~x5`uC8qTd_j04JcedaAd{D}WGwle z&7KPovW(OD12am0Pv2hs&AZB9)4;#3XBtGc>1u50gdZ!S^%deGbODXbGaOIs6E&BK zZMaX)YT%Kf3aJrXW6993C&1AV0@E=SPhp+e*Y_7YPPid9q!Ua?LU;ffI7m6XukpXl z6;#3bZi7&eTd#7HP3l4YPgp#cAhd%W4Vy41%Rv-y!4}ZxW0&9ATasm(=)&0YAtMMT>;BiSQIlJ1Yt?E}QTWY5q&PYzw`whFxb}jY{=8aQ|T^8RwlX z>W&9LhGI$~jnHfk_DvHp@3?-3(9`WC`8#%W`f;KOTbp19hyiDaYW@oRyHaPLWYX@b zvEqv$7MCc}pwk|B0|>RVv-^=kmlT(fps$U0F=cXCUe)o2J9%y-0@Z1o3H`dso_7%FYs{QPzqoB;H)-Idz6FPxKZZuYfv$NjwZMSMD+tp`2qf)?C zOmKgR8zo#mQ(p8{yaJ;|h3a@azE{bwVk2z^v%q!9Ov4H##?MntWppxUMpaWvOFIZJ zz*U73=ci7TughJI6poM4m==WCTAl3nK)ln~) z58mSv-)-M@yQw?2WWjeb7?3ngz}-|?LKvh$I;_?W2p65TcRqU8wdQv8jIN3)s_-VK zd2XqVjMY~zypc^(QmeM^)Qm%tA&7UOaWkQe6UsnO!Ys!p?Q?_`W6lzCGXBW`c*u<+3*o-nbLo>}k3`wd0e? z`vlzqIZufVY3b;E`3CZq)Z*+v7rg>Mn4)v@?d_3$$?K^k`h$D=wYD0H+0GxHay`dS z5nAZN3+>AnZx3Ph(!ns9ycyhO}9Ou1yDdqz_moLn889*K=#a zllu&bIcu%nj+fRI37_L9eL_bAB34g&KuMgnxilHjTkjPwTzb4o8^~on368ed&s?-5 z>cmi?CG~&1te13_Jx71ei^V`8)B9<;1L&Q?jozADB%+BXg?1RWX&f3`r)jMe-28W3e zPE>#N+}8MmlcQriCOZr=ybY+<-?VwfWN|bg(DeQv&UFbcVT<+U!V%+TYVzbWKhZ+l z!M`?s=UK6-60$3y2vY^~6Jfkju4E7VHPvUeDim^-eSxifTOzf}x!&c?*8r9?;t|&ZZRBlY`D-fh?eYD|bVlMNPlo@Lh_NS_?!;!=sB?xQ!0HZdE# zL&F8Lw(uE(&iCL16m7A5=m+R{zM0ns3H*^lT&E!VrKFhsS-FF-$cN)uwdzY0eC90# z2TLq@{4gimPn2f#WP14OE*y8<(`VfebAMfFmB7Za)vZpYZnZn4O6L-qVT!k)!7F5CyG972_6kI&`5H40wLQJL4 z%x{E?%k&qR{-exouBN9$^Spoutj)zdrM51wJSF6u+`5?pjBt;BoSFiI{jmS{cf%dG z(cUH4s83sr8`5(WsqL`3>9&vY*zkbyYII&PbFT6rwtKoMCJtXjO`u)p^W6Z4j9>DG#TjCGN zKy`G6ifVR}k99SbOnDc70%<5&u*IBh-Q2YM$v2ZGPi7I$?cr zz(j>)nyr=MZ_ftv6rI&4-z!@=#SP}_&25NM@yU3I-;XW+0PbKCA)mGH9^z+g9X_Xk z<#8m`YsVN$0ruhQN*_uh>g&6Kf4NMB2TqrWEy{O)h-Fas?DY>mEIq-WD)r2y;rCO1 zQ|j7n2&wEzZLNF`?%2$E-d^PtBoA)CDnEusSeT3Fdv&9@bfI4)%tB1_)Ry*W3N4iN z)G)clE5gpY{r+5R56E@6O;KiI@%4_{+ zUeU;)sDkC)x5_Y%&>2SM7dQCl9K$Vm%N^J;Fc#1O8}PF>4VS+V%Vh^LM^olU)tnVyW2{#v2e8uMv-c^shNpLQh3B=a&?A3Whzfp?%hg>5Xr zsidT&JH7A&nv7jq#yl{e$EW(=RC=cn$e_8Qg#WvD6v*xaX!AN^`5lyick^G!6(37SM|Ei}tv<6ObJtYU99 zxwV18*TipUzPPGA#O(piZksqO{o3PacC*8@BJ_|)qu@T0QP&hfyJd3 zb3o;#O$pqso{W}`3=?O5M7RBw`d$0*(VKY@>XJeYnifZvS4EnM8Df8EM*FjvBi^wf zd);Ae4yW_ElBcJYAm}7&e zd(|LMDNH@;z8M`i($tw7Cysjqel4y@l!!XoCeVHPfqma%C0Ak5`pR5oYb(Y56 zb7B1EvI#ETB6)fVn~nTwqTXupAQQQs#30ETa=WICjlbdt=T~?!Pk0@0+ZtGl#D;s+{O(3wt2AAvjbmBT*|qZV zRw)s=(UUz9HLYI@PLUlCIM!8bq!#{g&J;7#)XpDaM0{1d{_EPnDe z`ZZG0)-~3h#g`oJ_kG&aYzX{lO0xo(x{i+y z`UobaNmAHTL-QAwNlagzSRE2HT+JdQuT3!nkJerX-6I6tO&La$UXOLI4j69q~1$%tlfWgSQf+&j+yuA5HLOB|12tkVBMULmbWNVhir zl^K@yL4zoFocs4-{bLUCA_6Oo$F%E3pW(qtOEqM7&q+%wk`~&N=%8bv zl0;_?ppC2&Lio4!xwOeBno`D2Z%-}g9}Z4UPm@-aXVSj1Hz^-FZC?*_5)1JOJTtSR z-1tSkHH)=b(3dSW2!*oHrcto7)@id&8gGq$vf&VvivIV@xDX93YvI)nTQJTpE+VGY z$@<3Fug1lq?B`rKE&l73hjBx(osQDeGkl5yPS)jtOfyWD-#Su@`)($b5F1GKKsepk zOkOw2omYK#8SIV(wZEdj6)becuzyMM*PEWAc@bvWgA&lw1SBtWb=|o`f&(n5ywG(W zKlqW)*0&6EZ|y&s{0sBwQ8BiH#n-;a(%@U|cUAAP^}oN*b~LX0xfvKSI9Fv(Y9aLg z0rq(N_!T{F9EuZcFP>jJNYPjsJT?ASb2KE_R!n;&Nm^D>_C@|4r3)D{Jg7$-)`6q< zJk)|_O2g&^)<1mT*91!b9cMQWtTDB0mVZRU&fPXt5_<8a-L*KbNBMfh@YwQ4m0Cv9uTR{qhS{q491ls@d<-0>;GcyEu*6R!>`{NQV=91qy-7-?v|1=DM{%bx;qr< z4u?iUkd}}J=>|!Gp}PkdhJoSS{-1N6cW0gT{MLEL3l@t7bKl?ZwXeNDdrwru-0-G{ zNOmX)eHqYsIp`Y%bR{V-P(H_6AXEfpPFwaHE0)ls((MS^x3?L^EnFe|oU5-WQQ^jY4ccF@|e1+FdT}Z4kq|3T^ z18zo^N3HC6SE*wztt6V0fJ~=!?*pag3sZMekdRqXaZS|2y$xxM%MC**(r&A6v8UXR z<=AuNS>)cvmM_H(3knqY7!ZzZ+dObNoX5-1O;M-Vm+4v>tMqjAf^JN)pI~ga3k?;5 zK`50c<&$!bmszR7NVdW%!eq{YCwD-Ds%^kLut3*<*Va3_7za=-6tQVQ8ONnjLV0tv zBOI@H!U&2(TLgerYN@$b!0XkT9nn#Ik#YREpLb_ha^@cxs*zNww~1mu8vX=c{5}x% z3S2)7TdSFC_ow&IRWwCQ1S8AmDxV8=cn>~pz0ayR8Fr#r(NTOEy5?{q^u@3yv`=VF z!m64sCj##g3<nt9x-iimqlQ$;r6*8Vm<-$MHWo3Q+L%_uA z{G{)Xk9T@6qKHqs*UKL!xl}W@*M_o#gpDpjGb1no-9m0=^|r==-_z;s{|S-ke-i`x z(g7CYh7T1~w*C3H)IM_WKjZ+a2#}_qgXIRCwbd^9le!|i;^0Mw{*bvjP2gdpt14k&kx*;zUV4B4cIOS5c15~7mynh6cNhq98*|c};61!O3R1uaXVcC_432E(gmUTI z+U^Cf*@ITz*=~;lU{t%}mpuNgh7bT?r&YH0&kKyg^JG@K< z#TSDr*fM;N7KLwrsOG2dO~aqzMSm>>XFbLc*%8;_GRc4Fj+8QZMG;Dzo)E={gHUD0 zp2fqcnqkKb3ZejE=HHn>c(=TdzlzA@n_}j(@Dy3ijxE$#?_sQe8NxqoG|5VI3>;3= z{AG$#Uem;m8XDqU!tQ=6^l*EH0kO}|3xYDzrbM{)<>4&HTvzMy=hxv==bi>8NN=gpq$?blozF0t;x^dsy;y@Ml<%BZ`V^{D%mIvomd zPK}?+T%otc=!Lw0HiC0g_6dd~_{O-rK&gE4ScZ}U>z;tfjd5Q3GD zsR+ejM6a7oy-x}KqCPFih!qnDw_@6S30QqmZ*8S)L347yQLs%lvIxYP zoL~kC$ru8xXV((=e*fXaZ;w83E!usgUF53ME|hQh`IBkZeo|vJ2Ky%!VZW4P+E>J(Z^T`yVO4arMVDz4+!b0 z#td39(aw2s8}PpC=K5L;yCc9+CYGkobXM4Y)*&L?>v-vz^*A(HzwY4#!g@_&p2#-O zhoC=HDVoBD;0~_cVPn`~IZPq<8T&hNowpzl>$SEZ4@TcK96rOckD<}H&L8K{*UBU< zO_;f%{&=8jzB`W?2Q4x|<{-PSk%1AB4!bS%D|*W2V2rDu9+#kN8dEMlqhihBe#0UIvw$ar#AEShBCaXt*JtK@ z%wi?1Sc8Kc_~>c>7=(cRELV03B(T17@`y}Z2Fr$@_4D+Y_ohvGG0UQf$$YpnC# z$3)4?e(~%R*RPuFoyiO^Qdg&m5wbJugOGW&X_>uGi-mJ!y#nq7iqcU!kdETwK7Ql8 zJ^Zo!ao274)@0RQDdRsH8&7kDM;B}kI|0iVH!o+pJ)bq>#jsvtaFrq^Bc!$yILN0`_Pru9b3@ay@Q~e=udh0HyQx#Aq^JlBbkBY9Q0R?@yovPd zSZ^?j|JipH@)b$t)ix{2k5Lq$8~@zY&uC8CllH|4Hk5>45nYYBu3w$% zhuDhe11XI;P_|+4-gw4mZwePS27T_E0fCt1L&QAWG**aJ;p;@ck6nUmbxkH>9YM>u zS$nFx!uI z12xtCv|D|STARHW`iS4;76a;@`Kochj+e2!kT-;ZJ9i|c>^l9sdKQTTCuDDsms{OIK9nh7zyO!myE#8Z$NIP`gMu-$_>jlsd03h4V8EPR7+ zv8R%&Xgu%9Z@D3KWw*v-+ ziuFcLvs{)HXmpgD-9GL&IJkL(=Vr85!B#Iy!?yu)DlST%cLU1C$R{^KENiyM1IdF&fXqYPF|tGNbVngIY0>gwm2(bsW3@ zPvm$^bW%X@(Npk)e$x;yQi=pN=E5@E+NH1HzabF3c&u87>H03PS?fLxwhb@-7GqOB zV@6AFS`y z?|A$ux{>p~Ag;@I?^Sz|@Hf8uDZld>)XYZu&giHcq}e-YVpGi}V*|v}4y5I0AIO!& zoDdtSsx)a&QD`Uri4cGD!PVGKomaK7%FtKhIZ2@>qcW)T%SH>>;LPyJTSntG92Q!d z)c`D_Wud|;IA)qcZf|?NBo|T9l3o>nj@jxO?o~N#j-|kj!9&Z za#NQty$18DTI)pK9NLB#+_egh+p2I4TqiP$$L3R&Xbda4nJ-@+gA^*Ju9NwtLy)S!BZbO8LPuPJ=eDT2^xTJ1IP3-< zgUTfz1J$M!ReQgqYxppkO>>HCM9tj#>bCeAf5q^}@q3bf)N?ksI{VE+mA3Y)qAtcy zy#KSxVK$zrIIDy*u4LTwAB%f**!hBWE@vxxzaibjn1qI=(8{sPO))mVCN=f_?rr4O zreQdvLz~4YZlTIG?Y>|qhr)m-8{`PDf`T|EQuF)I9i>q_;`gK?t(yqsQwJmX9qPjuz)F>I$c%A=y3Ygl`AD$R|AYv+MIc zf_v=pS0L<0_~pa^Gz`_OL!4|2Ir(ZUV&Q%ukXwkX$Q8~kN#0f+8O(%x*>|{nXj?Dj4 zfeqjkI{mj=%*pIR5}EY=B>jp(lnhL4Es*vWJrQ^L;$&L^&=KO&0guDW^m5nHBArc2 zUe5e!?JqVKfu8pj_4PLiKDA1Mcyr8ew}e2X;rh@RAH(kqU%Vm>dyRIhMp|2D=IVzQ zrY&7jR@wC^kf4XsOA9f|zk`t2gj+tTt{;V#zj7h?@*keM z)P9{6xFt=nd2)iS3u*1&srs-9Y(Xry!_!6jszFZ;K@uxv zOIROz?x1CnZ{=O*t4;X0Y&tCP?!6$HU@{NNp+VD1Z=} z3=2ENr<-B}?Qi7C4dZuqJiMh)%nI>JN=m)~^gVai&T-V4nPfuD0(A^-x0?aisABmg z;O4}j<|ioDq3gL}_7b_e=I>SE>HB24+h(aJWr%w~ogJDX&7)p0ea>RP{ah`( zmP#tHQdKB#gRd`Gt5r-@mv5v*6z$3I2wHVHTUAC=TQ-O>Vt9J^H(@ZHpL4V#G~du# z=C@cpj?vNl9DiV$Qp^}tM!+4`h={VwB(T zLgUWRM2k?#Vz@U(F21k8Ro{`ZU zAT}N%y^O$KOy>~WAN#6@+vd}Xd-|Q$fKm|n~tyFawkFVbH(-T3zZ#E|Gzn4_FIpgOUG!MG*TlHrz zFb!mYv4U112Q4B@?`C2+jpJ83hTkU#dgHE+D)7?V^(ly)X} zw3m5BnbTfc^f&JFgVg8Zkn^-9cH4x89Gkt}NT#=T0iv^M#y={}?r zY^H-TzG@&HQjiV1KLbf*#J7>pGZ7u&P;f;OhFItVadW{ReU$?fr}!X5UG=O{#i&uU ziY}XJCPnb9jJ3;m9xe}gj(h61pY6wcexMg2xk;)OZ(?KclRvYAmf3Ub7-dTvB8jO- zRJUAlADzncMN_}SdkW#_WCGoPw6u1q#>6AeavwXgz_U<($9C}Ds!XAetCl&-c3FSs zuyZB&=e4LpCO-G+&9Tur6zjEArwGPY{2z_8l>!TEBsb*}>FsMvNNcybUHGbDzfVqZ z)Dmh!shK?)!Q##@H2iWP;xy}+-kshkV-)2s(0_30V?{J^n$8A7!I!w~sWmVk(MmB! zh&u0yKUmZFnpSnWF_6fd)r$sdHItj&@Ig!@T5dz@HhWxebf`XcKJR1;d@yB0p`6yu zSzf}A)|v=2ktBFoHt~BFNEEv6EYbJrvX>Zf(2yb9i}_(uD*u)j*yRVo%#461>CkZ zG9!+9;$_l;?h*I-kf%XG+Y2RI%{w`%2LlBDXWdE`3*4_0#=|gYEA+X`b$^^IMxzi6 zavhL&DE$WgwnwHz!1Fw0*cpyQBKZ&lF1{l5YH`9?tFq{8b8*OO@JI$f=IZK;(S#8{ zYYLdSSmoOggr_vanp$9bvY9jEIPM^Tq&UwOVO- z)O_y8hfiEX&2~Z%>V(skHowO&STY7R0&cw?3E=Y@)~m_~S%Ix|^sZvHV21=BAMq?9 z`hie;J@@^tSW>c7r@4yAs+it8-U1grga8j<(=|DMx`mZY&^h;`(OGRc_TWtry!g+` zFAluVx^MV&R0;x^4BP~43yI8%$ossfs)Cap>&nWFvRV=cg(Z~=Si!Voamm+slW$x$ zO5ZKZ;PElwyiWWPe32+D5?VuBHz4qq%j20w%>1K!tJ{(@13i|>kH05K_|m_mY+teO zJ-|$n@}X})CJA{&{k9%eHcgHTFl#5r!B@|C>Y*3EoxkK$3TgLpGd{R9HD@z+?<3eh zcF)lgadry19uu#nxqOjt$b03rVI~~8UgxTvA2dg`EMApEL+L6oCE+4~OZ^1%l$h5O zK~McRBXkr$0}j)ws^pir@y5-v);_j)Y;8!*_u3rFFmSyeRP7s7Q8jj!<1z!3%Wi#Q zxY}RhreD2J`W9^oFJ?0pNub3NmW1Qn3okc=cb?x6RExioH22t96iR1G8Fz{PYe6-> zxxFp1^We)@AFiFaz+i)u6!`P80H`?OFir4Sws@3akn6TFv}X@@Ydv7Ebyzw+$f``< zTdt*DV;9=|O)%Z;CtZR?G`2POYB%ivH}@JaT? z*otBDxQ_JwQgg9&!rE_+3K&$e?$|rP$k_-J=h+y7zT+fTE<~>{GuO`wB!RtIw=Fv# zXQ&6`_rjOzFtveAj%I@+59j8lMZ|(`8^D|k56CCl+q|NQKJMKGK}>3}x(V)GoMHxG zm$5H9y?bn(9g?+Z+*fkNtD8kx;m*h9)XTasq<7ViC3D{nFP9tQqn$HZEfa;Xks4Ht z2s9q7;Y&C&k@1^n1U7K9r3g?Ue62IJEtD ziD#cR1IkMiqpr^Qa0=UY$-rg`dsNRciIh(64|_-+{`BW*{)%^P{E;f1P-U2O9K6O! zC>OZz6_yoe|3~D&*^htb2bm4rF$NoXr-_#hXGSFz?tqL%T+mqv+JUDXM-kefmX7S=w zTj7`?_*3Dh78M?Xr7w0M%zX9b@$lnqk5;_URk{dx_NQTu^or?lmvhnC^Z&;SKwdg^ z_;aklQZ_H2vt_wK&Jjg5G{YBygC*}pM?fMzzHIUpsx;4^>(nyqOzL(U2?cK@lNvnv)B#K&^7FnrU1u5Rqwk4Su^ zE6UU7-uZ+xxZPs#jsEA8=6_4uM;JNdBLgWK7R!!SeP#|qRi_eL-I zG<%>#NFA9Kd2cGie`8_uO7dTQD?Bj(O$SSQ9S`6J-M>OL{XJi{=4)r>;sSRLB1^Bkxxg9TwBTswcg%re7+M2Y}U~UVWeBOTtwYJ!g#TxOl8y%=* z@b3p_3`evsopp4mW5r;%md@k(Q%OdUtrR7aXe3Ka#(TVbueLF&P}bq1={pfPxf*Ws zPUy{AqMrH=~c=^(vs%d-vS9it}q`L8=;N1{>wQGzqZHB zKl<1NX}8`d_NSkHI96_hPfiGDaOQcuRLFO1YvYiXwypDt!D1lBWjpMQsWkEO zecG#Ry0--Ve`4>8o5?QEw`1cm=J%z=MSllR{y%RP7Gl6X#Yx0=kc_x{Pf? zrwlu{Jz?+@TQ_;F0 zFX5!^jTpA^+%F|a;6L;0w?OB-hUIAmwaR6-7i#J>np#hZAO2z$r#-pVdgc^gddKtE zw$TFE&aD&?WaeZu3f6sOc_w05AolLpU6mlMZvaq(bYTuvsVmg&p%Eq#5Q9Y@w8~@$ zNSkN<`ehL7yuFbf8u0(Y+Wmjs!PCnC^KQQ&4KRZ=8a8#j-25>gc>O#xb)MXN>9O~_ z)!X)*!a54UYQAiUlpwYrbt|6S&6+><0BVe@f11GZbpyuRrN-PlTofu{zo7A@*!7Ma zWJtRFOB=>7*~@L5_S%lPria&$FYqx4G4u~Ux#PBZx(YbpFjh?nLP(q#H`@BcT6RC{ z=9vDn%usqT$||2&0EOh#))r1G9viwK#hejtgOUq}bGZY~Ds4Nl*Nlx_;qEQ~% z&QFkV9@k^{TFW+vD5MK(}KFJ9a0 zERS=YYXNW+`IhuJK8QHK{+xScia1_ApQ+5Qb^I&hb9Xsc)Y?k=%C8xa*Ixj^grZjX z*oX7=Xx^cYyT2X7Dhz1FmAjsn^ZoLwmmfbuLn{DZc+?>*NNyznL3fA<4LuymnvX)~ z)z-d5pQqn*d?I2eCUe9&gl|S@oh;tIjmK!=K{yiUVnj%4eRn?4#*N=hGm{F z+E)!O2wHD;+%?{77jJ1N-ya3tn^+pG2(>l#e2N=LH}Nmqx}DBNGXjMpe^68T!2u*K z^~Me_PmoaxznOQtaK92RTc_6dXoQ$?@EMB1sJAd#6^~1gtopWdiNpAleJB~91rIwm zI=|#xz0LaCxXE)9CYFI<$usNC59#G+RlFy|F&sjPV#DJT#COha)YUowYXTV1jx)v`I_=H^i8QdT!Z+T@L_2q0~ z|I@=AqViDe{wOE^)AuZN{OVMGM-*2e-%e$>ZkMXtV#BN1RvJI&+0GJf*sH&L)mmp@za{vAQ<28Nqy zMgiezOy8ju!)g?s-PCFUO(ai6kI)A=fP&Jic->YH-0_~5WFL~eHf4dxJUi_{?kd)w zJbMt2&~djZaF&<6NluI7u{{bN)Req24=?hbiD=?u%p+UDK2*Er&B4f#ASHXu%Aff8 zJ@}4o)rd^R4T3txniST8m>8bj_{%Dh(K*L#OA3X->6kSEjky^}kg75_{QcjCp8tav zR7k(UKjq-M9~V0AyUP@iulJFi<3?{zzs2Y6YW~~R*O71}pD!jg@MczwXAmV4B@d_P zF``?FB}x=RU<&+dm}$<#t-8R@CU{@owVL#*Cw$dffT~_FMSuWThI*`Bv00=}d%4X| zSMWWV)3er`>{LQNS+#;{IWZ`8FU~cVH7`r)c>KLSLB7^CKj5qg@%yq)ZL+Jl*O3>O zsYn!T(aAIUI!j)!u50rjx*lA=6D1ATC zQCG9&5PvL7SZ)<;J&Tl$sc4tqnjuq8x)oRTQ1(#>h}!}3l*6@ zc<9DPFAnJaqzc0z5XWOZX+z*j6M<`6#O|+-S~kzO>_Jz#$-rDT6fcWrwA`{hpHk-? z;IbfOWj8T2tO$7X^5vHb9i}On6NKn=u3Q!duVJIq@gc)6<8W;eb!MOEC$W!U8M}3B+XOJ9xo|Chy*{05ps4 zyVlR=Mv={(L7f#WCg?2_w0`mSmaWbJCLiy=CD#yHk^9>o@vzUSe5KXpv*btw2u`Jx9LfwrmuvD z$vqns^1jGDEN(wkUroiX*1hQ4!0*iFBynk6)a*yZlQQlOuFJ=|-%3b!9JrEmu5cV; z#Ry}-;X7~)yk8D4f*Vzo_jfI#RI|8V4>psg^BXnp)>D(FrbOSJd4Ut3&Fc!5{|f1q z1}-gw{NbIuIYWXnVX3f(V{;N@4#8Wa;HBJvOgC)In?gF#Pck_n+hDtIGb+$Muun{z zy{TUE1M>>Nn;R9$MB#VG@lO zjGV2w_E!QjG#kJ8=`Rk;fA~yD2(N!ygur9g`plb8N;GhGU_ks}1fadybRu))pPOg! zUDfa@tQjUkWSE7NL$j}O{kQqX-@!^jPzIqQd%bn^*qr6*0}?b=autag7P``T^42UD za|f3-nlClf#lptK3epZd!oHFXD8c`L$qniMU*cas5p?_u)i1fbK!FMA_Q#9IOXO7X z_*77alK5{OTv1(+dvE{1c=JpJGiXh?YTr51j}ecOU2;x7jn|6g;ZV2Oe4Ee1Z+{bw zHR>SGO)0V75`sG6UyRchQDrK6?UF3*#)64pNR+#1+j|F`U0D3S=ZNZKDsC|#V`;bb zr|hlk<~+ZnUrkL-Qd%Mpp!EwluNdmrLzq?(43?-ky7SGnk_2w_<1+U0l=Xs-qjo_+ z<5i|?P&-#=pr)o~@YPrHFKk?=o5^1-0XH4~_Q^t>u5nPnSBHQ#pSC+45ZWZ*@+W-< z(AS~=%blMQsg{(O7_#yA8-Ct>rBTb~)VJvK?XOjUukUO94;@|I;VWLR!Bl{+(o1QQ zbbGs3izsOuU^FCP4iOgMw=aN-`{5Kb%_PZko1BAsry-2e+?pjIN5>!dXqa5XS~6QA zHjQb3p`jEb`kgRah6pziLRtlZJRd&7cOWUy*3<-&@KuA&ZaZdFke8_D=6PNo9-SvN zGzn!5nuNGk<2(tmG@@P5Ro1wB(w(ng&O7pnWn@1Q(a9sdRJF7!9Tr-#Er%29FUX~H z8UF0f>|ExQ5Kmt6Edzh+4YKLBT*(@9dMj*Yj2-*dzbcZ92^xH|P?mSrpDyx<_CwB8 zy&Plf)0Ps`)=AH1W}>U|!E$8lgbpig0ustk3(dh})~Zqs>=)EaPf5W>X35o7hl?!B zj_;?I4VZp-@u=G)-`oDEN@|F-vrtjWo!gtv2xfh{Hc;?{gqnF0uZ%QV==B@dZB>O? zE$@v&*RP-S zPlOlnGJi=-L6}L-MrTv!HuMxl{F-<-Hoa}uk{dE)4?!1R2pt(KtA`|ZBcHUa8>Lz& z^fU=RsWHd8Aw7vmf<0tsub|=b-Kg~U*p{^jzK_E>`A=Ka0B%?5}i2c^xdlep8%_7UQW*R{j8QZZ$Ff$ z7lD<0l)j~;tt0}n0=UwooEVLey#>hyKCGSYC|WoeK1msV4Z2e~Z3HN&oKz>6hnqbZ z$o=nja`zrUR1IIh-X_HVW?|?2s1Y{Ya?+07bg$$nJIeh7zvDy}BA4U+5wrGhRT3VV zz~fK^QljYld8I$PcgOYWP~YdI&1-AHI%n#fBWKf?5p;JEtC!CF}!_%Oa#0?*`dTLa%t8 zNP81(zjPsIX{3Dd2?)oC=oT8q9#oGfI z44oYl;u@5_!PiCQb#x`JFCSBYsVH`0V$>2!=PjZ%v$0fVAMS*ysk%S+{M_F+pKU7R zRut)f3u$*7uitFCkMBYvs+K3>4v7R9UfxR9b7EcLx2Mg;dDHb3uk;Jr(;PQ5{6;Hc zU-T;swqPSgyrPgDZN2O6mC3XLMW|9L4GM9(#XB2ejj9^|RKwGxS= zE7sXUCg8Imm(5iVUM;QKk&<(*kuO&g%TJZRIzNtDt%3yys}Opw=IJ)lX@sEO{-b1A z+3`Goo(8nkt8a(n32+Bxy--r&&^;X$ZP`lJmPvlksXGMh&3UcIvXd2qPeq-UN;^bF zh1I#`<~G9N@wDo6e5k?@^b~+Y)Hy8w`k(70ae#t?lw25(-1W>k8pSYB)sd4DbT@aeWbErw z{^h-~F%tv&jyYoU{<xRTCo{RQyJ&{RllOLb8A{eFY)yTXEk zH_g76-=Puo`V0EO2f2w$ONK{{LqkKG0QMy*C8cQY_Nejg`CJED(`pRLbAQ8o&-FGT z;_J=rIa~&4uZjO=3NWD2HwOZ?ZyJ98#vqs#c(l@Unab}k_S44yu4(LhXT-Zp~t&wKXwX^`+0nP?p6w9j!&9&#avqYHHcy`H? z4+7cnB68Q}e}Q)q^&9nK6d$y;*#bW*_KndyTr-5@%=41=V@#x1t3n^ig4aGp^sXZG zyf(a+s9+Z)8wAe+_7ga}uN^M6pmST}R}!0lski9*{ru@2{Chtj#(f@79ZY&>ddQFu zJd}1yv8$N&>KgS{oGj za(%f^VTsr z2wJt%KgW-K_fvZ2R7?7VM(RfMvHk+RCbly6n95Oy2V6w%hI1-12I0^;$ib16md47a z1x)}VTZ1>32jJxn_MI`41ZI_ZH7l!PtH@mZ3FzOhFkJB5NzYS6b1h`e-xil~Vr7LX zHW=~Y>tBn#S|tImF$d&9Pyl0Y0Q#F$vt@F{hel<=JCab@Aap@WO4^XyxC}FmNJaYm zJk@FSBxs0M?7k}KuH1M7IGdD3Zni+g5=HdX`@DHlh87)!6SrR;a~9#vwRphO9AGty zJj~{IBl?1tU7{Q-RQYMN@PU%ucY_eydv zZUFM{^C41B6yoGllw93NmN*&pu^WptGZ}GU^OMKKX&JIX^rp8jg3*psv)j{t1v1eT z+)2r7L8(A<1R6v7t(nbfMNx6_OU^3}j!35N8G8Y*V?&>tvJw}=@}*Xvx3z4RgV0cn zHYewe_?0#&FdSHY4O}|5gNWsRAvkoVy0!;7oJ$&3kCF|!)A0&1m-Vq9E6c%9E$K&p z)V%kDwS%wd2ELfGNfLFxlA`FxV+_hMLEupR;Luv=_qGDbRi<=4WqffegMr$xCK}&U zK;B3<$`m9Rv(hogx<)?PyG096tlpyLq{t^2Y)1m_pVcs{ybYNQhX34g*Q5bJJ`@C{ zu#;8?p%b@LH$qEKkqLdn3*pZRgig#|Rup%m{0xt>Z{|9IN5MYx==aeua7%o zXspaX@YU5~7d-)5Ch0JTxpx+)M>h0ewD4ji@>I_u-KFB+;aD*q5z&~yk5016@9;(@ zSuoSw#!w4BHtg^?NRvWtT7gb^?RAiaD)GbFQ<7OsXeEo9praXGi}4i`3&imW80&>P zfxxJ!sOUjjrno$-7L*-0c>vG~#h?j#qw4&uKUWo9FJ+N14*m@*qc*}TDCs0NeVBJQ zQ?4t)$o<5FveuAn>^{EZRw?lIOr`UxF2O7?2?Lp(O_!9Az?jUYIlDo@WtfM8wZCTx z^1BZJ-S1eAX3f<4BLzUGZbE|6RZ4QX3t0B{c3UHRg06T74fdQ>19ZK=Ft|AY)sW@~ zFbha&FvadW1DjuaE1uZiT@AixRC-%xYPa zH0}>LVPVPVu=}G9%&gr6ANckz@?d}e8Hr`Cm>C8EA@pu6Tapdt4<~dt;Hv6C|0?=c zf<%5S!G8jkjr&^g_(9i0h;Nz#tZ9CyG9e7>0Ht&g-Ur29eI5!t#)rW-%k>JQeI2E*p(0QE2b zmyug5Cjifc!5Y7hldUUdjF#cyf0PN2Xp_ndMXgPsS>Le*Ig3`a%}AeeiTi`Q6(q4v`s3i{!c>W3?D^r%jKAyWOv^lN#H&SBm6iLWB3`kGw5V5RFnyCqUHC2@;UXwBy)2uojpF*IzJuCsB;jL zUFFTsunlm@Ppril+&RRotat56J${7w-7D!3-{Wbz@J8G#`0jl>Bm2&Z6!9z={;fY? z9HMRTXSFtRmU#TohDF$#J#kEREGVeo7fuywvD9}i_zEhXa@{K18CVKhQjKR&cRXC$ zPzf;=%auc_G7e8j?8x7=e!#fdpdV8S^66kGnIq^#`i|e^czN&89-|Q-R%*n?fJo}A znK5KF=jzXgV-MT7;0QPEt~1V2*dK=n{JCT|my_d7YgSeF>g)q4Y8?#g_xI$qp%l7mzLu34Iq$w^BJTRjb zP#=npjBKkrKJhf+kKO!X$fNP649mV>b9B`??|=Qztv!*X`l&;<)Db)+*_79(FG=A?@FOD%Ft}V~klB-*mb>{dOsw zN&Sh^+TuzY5Dy!q4#U%;GN)lX^lU*%JLk5qP19}Ma(gX#ExC2%h`Ki}kBM75#DJ6C zlu0p^wNZu^V-jMSBw~UIATPq zSmsCxvfs`&HYvVh;L@rp-3OIemrqNz`EofioUWa1O4IHBe9eu=eV3b@&UwU+c~VwZ zHn{@gL@Fabf%Fx%JW`T33l5kU+n3yZ*~UFENN{ zJuSg<(l?TOI`e`9!ZGdQf&i!g6ej4r`*#Df8TJG3s5EeM5TE4x{eIP5%wc(slrf%a zUXZ5K-H=vZiE#*=lSU1Sdfdcn(|3DShnTvNo87vz7Id_lMHsL#) zzVsZu6C9~vCHjRC1`s@Aiun#tGM8wlvFL!e^a^WNjn zEvdg^V7_BSCjUYZya^H@s?PIl;owunXYa&@KpQG?CwfFoYBiB(*42Q#6D63m!Zw0w-BK<0Dks!u;jMBUw&b0VfN;EkkjZVvH zfYWgR7?q0(3NZ5g8>G&zH)4=D_9b;+b)?>Y_+!Opn46tSr+46F#rS)2 zem+lP2eWg0WMoRB@#z~g%nMWG1I39bc~8#<2~Hb3r$j|uqCo&?#sf6Zr%;;AbDQ_Y zy?4Yz!!xtN*%^s$GQp+5r_eiSy+KecC?PWLnV<$a)e|wJ<=ycE|496qvLM`ZNMLT(^=C${Q(H4wD53VyrT49lhX3;OLF~9^Va9LW*ptRb*qz%W)1u!(p?X++`d{^H<}4t6>Nv|~kA^1Y&YMd>dyt>2Ze*f9*i zvpDLkCB=fKiquGK!s5Da6IC0Jv9-TkT{R9K$-G_7VduMs1PW!9GH~;(1PB?Fxy__6 z=9^#^dD?2wPlnLll3P&c8t*sxo2Vz1PL%D;etM4Bn|cAWr-H)R@Q&7TW`fcMka zID~*?&`mkfoB-)LCdNi3@~L9(Jsmc&a`ZAXvw49(DT6ESJ9e<*a#KFIqM6__rv~Zl z=;CZhnuqO(@@Ei(cN18#L1k!{El#E>CiUgwca;=z#H3^y`_rU?pR>&gsES?z2g{SM zkL6+^vv298vK1Q%I}NACLGR_COqC}O0F9*eWsQTdO+OW#NPw3cdyoY?!O&mYY9q?2 z7fsOz!yCkwIUSoZZvZV|_9jR!IVovirW$Fz_f4qa{2doJ_o&kv8)rcC!rV#Uua9+V zcgXbHrIZq{v&Mh_@WOUXq1;@ykVdw}tN9XYdSA;ST3PwKg$rBL2VWoiP^m*n%<1c? zVp-2Ret#M`f2%lZQpg?m0=+_nq>NdU>c;WtbF|Dz#XfJwhMKKUYxJBaZlAC^MsN+Q z1I8v)wuKJ{CmSAt?$YAHx49{qe8-!Pdf7`WXg*c3=#*eityVgnL)y_MuR4h^@x&hN z`JO;<_fYH`8Nrt%n{Or-kXu0qLZ0yd(&vA}_EiLI43Cam;OIsnF3NUU33=}y_q@G* zbMl(Bh)GDS_ohdPkycB^^Nsof^=?b?tE)k9ncq$RZ3%GcWqKu5StjN@cU`sSCmbBc zS+F5NcYPN-I|2|>`hL6yd!ZV^%erTO@|EpyN=+GKDY)6ev`kpgN~3cwdAl z>MtvXqKVk*+Gipo(CA~2r}9f(m@xLjr`E(dB?{W{ZF+%&4R)NdTc?FM4iw9L8>8K3 zV2HKLGS^AVZZC}#;m6R8e}_SyOF}_nCXif z*PHcK+kBw?Zv4t1dA7L}QUMT2`2Jdn!mmR^mHINfc**5Y^fojCRwOwgKAH8uwN-y= zMaWD(e8c1ACCS=3L2#eR-psr{CWb$;P}!153ba-%Kl&ImLJpcDK?G#PI@8bLS9(Sb z`r~BouCamHQ4~HOxCFt(Pf|r{?gDMOIu12oecIZBOe##XL)xA~)po&z_$5v~v0joUnj1Y7)(kc-;@@GYUX0#51@ss-_7t8sND zsIFO+kR?qhiSw)9eguDhhqk}P4fJq8WSWb;rv7l@$1H9IUFXOkfwY?@>h?AKrazcw zzjfe@qPB$(Q6y4uW%#IgCageV8bbv08T`6k;Sc&;q@Y2*scx$BVejWMeJwgvEJws3 zOhOa}?m~YaeDYc5?wlP-NDq-qTONsI>;o=TJIkWr@#X7RU#oWiFT68Y|KCYQi}@!s ziT|8~v>tr!2{&-90U&(gCM^csufK}vk%HS;gfp^uzm5tE_X!OAsqyBXp4FmaV!PqIYQ#2FE(F^g*o5*g#1N{F@Z(hQ$@vXwPAx`EzLx-*v%veu`?Q99{qXXfW9#1{k?^nv< za6Ud}RHy&g|L{ge!GJAb_iw|M^X1&Ui-ZRXSq9 zlGsb1^-&s2RCR?XF)Z6>;#&x#JbgCH1E=G<_tgsTS~<#8K3Pe6UI}k!-g_#PeE@{LSaHZ-CY<*Z|~Ib(AvVXqm$2eb|-jt zdmFNUf^VC5EJ$Y<(oj9u8Vb9T1&DhWO+ z!{dJ~8phNGcf@X(LBR+5{rxYL14{LbOxa$8ot&I>V_q1+0}>Mx?Q67LU+qA_i9$Zr zV+s=c%+7fDID=D?+3NWZ&kf9J7PxRm;38fo)oHhX`uo2Ch91P2l++ws zDyHkH7QW8X+^o?7aCcu6OyK;Lzqcn`da)F!z0}b%xcvfDafeYak(xKIhq*f-Ops75;duAv<4ds&ZpQV1=IktgMbAw zENexSEsfg&J1CD8@ciNBZ=SDK;j3#iCUUWZB+Gf(^U8$qq|;W&l%+PsKLGM%ktdKG4b-Kz@r6>;_P4j}TYqWFU#lS>MuDe-!vMvSfn zTiw*T?;yj4iCy*(740|d;Ba(v2&R%%pSN+Sasy!lE@>GHLlK#HUmV?sdPBwMFML>A zK6|i;eDwpflz%jV`+=9VDNN-aJ3c{7i;1iRNi5;kyp~Wfs{+`et)gtR5kRa^t9)l&LD}Dy5&u=4OuY9x91sxV0ojV`k|NqzZ3QU z+|1Yj`-dxYnPCCTPk&kz0kF2hO_u4YiJ_(F`9zh-ug+T1x97X9OY6Jktx1nT7+@?* z(4PK#;qpsL3Q?w*=E3-s=0fS*=wyh-I6Qo^%!rFYqHIiqV}6h72{Vx$QbO8eOrL$Q zsmU0$=Jw2J#+$1R+JzN^$+Co9vB#N1sFiBa1(=-|)wZ~Cu#Q~N=fu+B~ zM3+u*n-}K{eE4X{6LVhWWgg+rpWQrPR@{-~stq^wqM=?6LiN1-;cxzTO%B|&Rl4=j zLSVLuU&$L4#z8TULhlHGGwZ+)K2DE9UuN2@{K)jJ;Gt-sDr<{yHC<3TC9z6o(T*6Y5_#uee* z->>*uhewpyJw@GVBGw4|xQz(53h8lBv`4#t1;6b);tG{K(j2w_xbVhHcPrTc$HuDj zIAG#XeG)zXC$?>wlwgFQa^D*y&ANnje5BmH!9<64_fUIIz1?=ebm!!3ip{|qpqSRW zL(3}FZB!q^g5%Z|lJqG4W@fC(CevS*^E@BB%z=jCiSe@H$f?=nm~^SdoRqq3u=D}> zRzSsOamP35tGqjfaxL!{=Si`!)&SZi=@0v#|5|%?D;SR+po+1>M$Qv9wvmQ0(SDJ# zGc7GKlJ$>cO8!0;dGo8)6ZLCSs6&c>Ch@nJ%^?k4UN0(i8&`@VO+N1ONt18EChko- z{0>`i35jro34&o2#qSunB#M#rN=uby9 z#Y+=*3dKGSbvOSGQeDRgXsw)*=o-$EwfC@ISCrf zc~Zq?d4a9cuPGw=edC3cIG`Ti>1k3UIk+y9#a($Th6oNVbY8rU`;Qu$l`bV&4&FG8lFAS{taUHsp`1i%3+;p&Ps6}>B zhP2Gey0}NZXF1K{(~BWPni48qwG=GzRNe1n&aO^V_C-;V#3TNe^5T?%uWMWqTwXA= zS}i{JA*Z4RY_y|yfe-_slL-&>n;%{3daG$uv$O1ce5JIh`%)|$8ynA|(DcQJG1$~v z`2wkx5v}QA8OlTbxk-j7T02LF>DT`Qm73?nqxzX8LZ_6A6M2oqN*A1GX~VSU|2=AP z8P)by4%T*f(%g0xypj)(m|LJ%pYLRytH+|sevnwNPm$;fGcD$BpR41npmG_)v&_jCs_!b;P(_qwb-`-V2zeuRR zU-arEbiq=8sk`q|Yl$RnFXn>yxAKQRIqf9q2|6T9=%zMFulmaaClosBB^zK>aG}G} z4YUx>9@by20)v;38^cclTNML!!RG6QLaCOtjV?&KU@DzD_QFC4)f~) z71k>31UrCDo`GX_X|a3XukQ)sF?-?3fcxE7*jVMazQ4J-%78)o z|1D)n(Bpq@2eEO8+zR9%g+nJJ=I;XCHXUf1XXAK6SQ2)C4Z0)&yl}$)#~=42rIt9S z23$e5d~rgNG8y<=X%vF6Tbnk3%B)RXFaOgLkr>6mW7Qp{C)ifpwIXKP`%8V2{aZ## z3V$GMRshIt`PF^=0+{}M=}X`MN_(rE>q}AqnHK*J45F3xiIO=P#!it9Y)UGA5P#sd z(DW3*?(5pz;qzA?Uyu5z4kOlh-_1I;@ZmbbA1mLZaZ za_pKm#fPgmQGc8rRH^ohik^>4n?B@EBk#fS2apQXjnOhyRl>QH8|CHYJwv=pPw33` z9%rk?cH5TuG#VLkO}rM<)7LLz2g|i$?O%dah%S}7<@D9G| ztr@QDPwIet&L zGC>NF1)xlKsfBceqK&fB;a=uOsFM$EJG@WS zsR<68T4ZCHr>iWZo4PPs$}PX`SdM%r5Tdq=3xH1NRk}6CU!*LW;W~SYS=n-hK!+Z` zZlZX7p z9hk1>;1aKMH9(X=MIR`q0KcPLSeEPZ-!%I}JpedY)1%XgnVuTx|w_X$AqS2h4 zd%Mw-0aal(#M+_JAXvU(kl2c2)ix-3^9XRzC`j5lp6pxoX!iq}*E(P~jqF`08iuOb zhKu5~XGpk}$=pu+^bHOI{{sSjxg=l#GZnLXijRfLc^z5pz_zN*;=IwYOI7DxsS6Mi zs`}m{zm@D6@ER9=Gj;ptz<<`5Zx;Ts10x>s4Lh%0L+_tPoW8TqW;{m5{%xmyF9ho{ z43G7k44DuWjbl$Vi+{gj63hD;u6snb|GAeb?O?vX;D_xdDKGzau}E!YoM8f|2i}l& z*tpPz#PIDyE~mJ-JjADc#GWOc0xJH@H?dVpKAW;kQ99t=b#vvOWx>y@F)7Ap!ZxJ% zr4I_9Br*=Nl(@(L6urSaxw3lt8%dq2=2zlRzSzVHK08^F%x zEXIS@;Q_Qa9iE``F+id{aB`LW{-30JNcq2$wx1j?4aHEy14oO1qOEbr%2nT=fBKZ_ zaWJo{aA;?<`u+&MihQQ48xIH%y%RiB>+>^c;y=JIxbL^U+Ysi$Q-R%H<8cX;#}TWv zm^a&gkhy0eN29x6O%dEmO0hC`-^gsu6dP)3mXfkv<%@#O)T3DCA`(D*zcPe^ivTVK z5lAyHU2>+EgG0YNiiyK$M)DXvbhQo~W5QP5qA7w|Dy|EyVFhQ=5W(TB4db7F6c$%X znB|*TK#(7+3fO2g@t8b`}|_bK!m&t z4epe)h%XLfc>};yN|Qk08sM(+{C)YC6OH@f6vK@5wCXL;pTy^CKIFb$!dC|&>=}V0 zWFir5vFY^h9qse$T2xbmRa6sr<`|U%4U+}C3 zWe$S756C*ZRcpx}TdAW-<0~(%9!H5On9^mw?ZfC4QqLowo$FnvvGACp_&mkFPU$ zV;ig~O%G0^@BTcabcs?4F19265zK!mKg_qAez3oo9$aYsE=TG$t9;rRd`@E2QjZJv zVlcSn(H6R;9c7O@q0ocy1dTEclGmo>(UQys+AKI}Lt1x5$a-TLPU25OhrsMNv}6|k z2(b$mqT(5kjl1*j_XYP8~d=8;Lrc--1RjN>G*&qs1jz@SkV8* zdX*RiLww#9_}^ZDU7_iXE&VJM<~K0qL9my^|qmHKPrWo>Gnnz9C5$o2O#`O_a&}C(7VO5D)r79%TBZ z8KtBUkD4ptUStDx_C|4TIj1oo9QUdp5AgwT#_I$_UC>%!)-$J7#z>z@VWdc4&)zRC^_)a}P=7;Td_ zMhc_m>b0@1WCP9Oo@yp#GG_D^>Lab^>h>dj+8zL9xD=TA0vLKN2W6U=1#BC&V@TFJ ze-{^^UpEF}syZ>L%!>sIe;M3sOoK!Z`)i$YcY@$CSb#R4*{45>O?r!& zoSW`_3T*NY4V-L`VO>rQ04ehEL;1q3)}cRdd*rtRQ9se?QS@TO=lh93byWz!f~)i- zJvVD!q{x_wyPEL){*6bBQ5O6d>gPSrt|r~Kkf>Xo@P};=1`Xljzq9Q?jnWznTEaqUEWaN{4xdXQeQ}mP z3i)Y>!_jPC(H2meH0MT-Ty=#L(4UQ3=2>qrv_!n4oMQi-LSQocBVux-y4TT0lt@5= zA~mz4&j+PT#hEV)$?vK^>`a<&U?JnzEfG03$~PkO_=q8BgCqQZzx zzP-JR{W0?ql6DKn%;M>L%?|@E{qMVLJ$@y*)FZX(VEMD#EjNC|fsgSJ1WNS>MKlRMp+8FACJ(3`IMn!;)=5KHHEA=w$Fmj}4`Vo3j z!Jt2Q#H#{X)H?VT8mblEbe5~dhI;!iFE-WaV}w~q)_e4tCd+RB-(aaYqrTG?U)ygbfIJ+==O0iEJxOGz<;FV*+PPw^;5>WB-mxlaQz0& zpwS+=xuw*)JH|K=E>rZtFC#-^j@sJ#oSG7){xZYI3tBImvnsJ1#Bs(mfaU2J_4TWF zt8$S+cVTH#{z}X<$AUlR1v*;1^^A;4A;EyQz84S>@h3wFHL&pC*3Fs=0n?67 zAm>UDjsH*wX2!?}KFqVx|JBLkWcr!p#T5mB(W{lIMLYE2YK%-n2q+H79TEdvhdIF2 z{%CyvlaW%7mLJ5rTjhO6R_a*!EV|&k6jN<3NID6)keW5nsW}2Y2C}t&t`>mL%A(e_ z_sh-!G^g6(FGUFU-+7-V4bwWt`bdRkpCRpA_TrnM;=e#hFgZBP{tRv19pymW3e2E1 zm6fxlXr^0TL>^CTz zdgJx4`uhiJVt$I_ybVJCtskA7eLN&nt-LvyLgH6IF=h63iR?Q3e9jtkOueJByxf5U zay_=0D&w#@+e>MZ-@B;PKVA%E;dy`DNz#0Xv2P5>RpK{kxR+00mkTr;$H!>v2Wez~ z6pZW!zlAov!2k7?x*d~Pf5+fSEx(15n{SqE;w!iOU#@WL{)W4_d`l70QdZ#O5yU0F zC!VIQ@de_qsY$Xo%8rM?h$td|{CGG0GZI6#C4;@hy*3Mz7D#y_$AW~D_|&PX*xeos zGOt{N{K!%>_dy$}-{~hh#!HRsYvuuC_7~mXknbChd`s9~;ea^`ii;D94Ob4iMND1J zN3zwWX=U%}ypQ}eI_l)HzNOm&I6L}=xiwv`Dy@3j#nT_*RV4d2D*1lD|2$4KAW-;& zipzyh+SfPd!;9b@2Ltw)B_~1mzGXI+nZjd%NTXXvXtN8dOQUOL{$2R z$vM^X1(un+L7>;BUc!^py<)F$X6wy7Ry0DL_{vZLZ#8lr)GmG9-mNmu6B1C1*FTzg9f6fr>4kujEOWa!5x6 zIzqjrgv!_aU(?*WnY4Cjxa``2IeG|@u(AKtZS37r8jT$&?!y#^ZwEo_t!om#j{djLtV0LrvkSoEkXBbU>>6c14MkrNJq^Fv*D{g1_pVlS+svW`V$K;oSPUWJs6j-u~f$XsQV9@q;LeF7@zxtVsqInj2DQibu?-;4 z5Ya*#0X0T*ALQz(-AQGY}`xcKdeadSPh) zRYO73W{k;CFI6%|aAsy-I4MI8bvpp0f@Uude}>;62UAEnaG5+#dWrn+T4L{qRKH1w ztw?f(dxr2B4lK`SVx_jiRj~TEw;;=dr@OfYQ=0_83Y){c=4vFj86#=ZbbwQfIQNvevs|EGtvFBoRFL0ShZW5{EttFJ>2kA7<= zPn5B>5AG5=pqGcxG=GwT?N&`Q*BI*#iiA)MMI0P9~c_s$Oehe4unyAC267Zj+Hw4Y`|UV}d`26v;lsK}z+pm$@ya6Eouw$^Fc z#IdPa_wzVjLl%yYd+|Kbp6y1A?VLFM{o$kr|Xf#-CZ|&LatAnfdcgH zWcgAi>y>H=X?HXO&pH*uHmp7!^xm&cw$8Rwaky`2Yz zhGty{$wD_HKP5seK6nGm=47*{;dL&*Pj`hMelItx1rQR?L&*k{NSXRnNKNVMq_@89 zoiCAh(aqd5`f%du>DkrA>0ubf|Mrv>=D69T%#!xj3q{_?6KC(dCHR6h3?|^L{B(yW z{#>D`=Op#yU}Jkh!dTQBX)@%^ULHLtDTKFG*xGXcMMoO7>qRCPHr@mHNdQ*Q%U7H& z-0+Kqd#d^LX1|sRT4K|=rIgBV>4P4%yWu7toNr6zlk4`duRPS4UNB8L*=iY-U64&% zk^sZN^)X-f%{T>VAy7X)CTNVmoyw*4Cz;srrlcE0{TKP&03QEe5BD~HSTFJ2s})v8 z04=X_X+^I518Q1g@X}m0;^K-BQL&8*k)^9G@AAn;Q&77ts3JmDWiz+AAOCDP{9N14 zpPMayE6as*=4oIMe#P*$149vEIX+(RZ)L^CcsQ|&`tY)9Ju5=(-FdC>e-o7t+X2Z$ zuBjbR!vihU<0i*Fjw(pNgR{5EH?n+Bu?_YgW-$s*3|m0 zP+<;IQ5)zXx#Ut~=6F+WRO0ndUby7+pX_+i<{X@ICd7OYDrV2Q6SIAM;~ThDM?m%>Ej*+J-qZR*;UcUh9sovR_e}Apqa~V<7BROUF%hh31Iu ztbJU|*WG+>RQrzThu6ctW)ze+Upqa4BO^#}5@%3=`%lvBcW(c+zvmNl?SNd9%h_Jrs*n*Z&;oCN z%m$71*`DO+{5To-t|91Tr6-bG>TNpgRu!(Q>ao3Jqi`(=)WG+)zVOw%iScaRZQHTn zOs8;_ANW6GV%4EMpvyM9SuiLjRDrocm-EvI^g%!eBA1rI``Fnnb*sz52&SfRPjET| zcaQmh95)9){#n_eTG1iZ%aR`ztm*euqvHnc4=!%nJC*TyRPrSO$TUgJHNOv=dtDkThDl`@!H0<0;^N{7 ztSR>rAu?u}CxGer3t$D+c^2R5Wn)p@4 z9+98;&W08NbiOs1pzeYRa~px$*e!4Sf=mxqGYhtf-BAbsGB5v4mW$e1{lO9lJ}fcMm_NF@wDiG0o4NelOH2!Zh?P z=+5z8)J%ojthW4Itj|iyn9W?eM9O*>m6Uv`wj2Bhzo_hPgnReC@(-BON|9sI!u>{e z`HVpJQzP?(^}nqi#D4#X2W3Mn#_~NIx_` zHJBbbH9hNi5hDP72p6O9dsGbN0b;t{f6TYSJKV8vwp&NUeZ|)q^FMpgLviX`8?3>a= z;Lmz=(EX%4|3aUZaj|H&MB2YKE5V8Pd9~%aJ#6@SgCVL>!F-#s{Z!LiG=h?kK6E=f zc;91r$Q?Es&cHUo6;UlxWPK+sdLFSL)8O73e7fwt52h@#I7Jb@M?5#`v{1oxM zhiRylE6%-Vsa>+gkLxTgBo>$y^*@{tT;f@5F@&_2?G-{PQa26=Ayb|}S5T(uN*4o+ z@EHcl=hpT+nz&bUz*S_BGuw(|h>u6+*ZRZp(f2M}qo2zv$M@YUpu|JS1EiO!03=$C zQ(i*a7@-aLg(lzK0+kQ$y%#(`{##?t^>0f2p8h#_$xZH*;bkM;$B_dD`tLah1})ch zccAvKEH)#ncvaqWY>`njW%2w(%)lg+>w0){k}F>;{R_~g57d>W+936{?X6#ut3pt% zp=2GKoUVsM*u#TcPt@<*+ya%l2g3*h!~JNh$B&(p5~IG(okshWQ!EE(hw>I1@FxR( zLa%n(GzRcjH_%BOGqME6VCr8UaVYX+S8ufy%X>+{!{lVv=L8E<-zx z%x|d@EB4Ewyl(^z(`Szw5~Eo1Q~MaBM(+SkIj3JMmoBS&zJjFbMxR&=cBgN&dy1l@5-+2I@Tij6v z4V`YwkJYBmt7y{b3OBp$KKt|1Qa-)6Z@F!3Y$|9Rcpz zJSbxd?ays&&`l{7viaNIQO@U^sc!v4@^_EPA9XUV90Ed})5#eyEX1pJV{Hm#+EHk( zDX7rdUjU@EXIffy*4R4Biqbzz_L}~|JD>|~<^#>H`}DO_yA{m6e+N_%G?@pKAOX8f z83`;TOUO&aBVcVcyi|^BRJId-3)E*eD1o+iqG>uVyk0pLhrPuXV@r6#8~rGpv^Lw* z=iqsI>amsrUw@4Ec-HfxEUZVtO4L9#9Z_8@34!Wl`UC{!>=CO(kltDbr!-@a>MFh64t)gka++*lU7fOA&ELL3-F-Uu+sRU?5<}ND zdNtEONkDd4=gjmA7ppww=(j5ni;hmruum0G>l&41F?t=Uvi_3v=#5|1{Vi;JLc7o6 zj0T2LeWc((i>7@}JJ~d%`-~Z39xoK7oCTB)0>I4lPFeW-5`R*i#H6Gwq+=K)$Y%e1 zb$`0%wcqJYM#J#>^#lklU~iVJTgLUK9ulm~L1x3rCRP^aiB;BBC0t#@mqLb!sf0~U zPC6LBs^W`#IA93ka0O%-`!HwIu$LKfubEV?Kb@|pNmeflx|BM;PJ%uuZ(*ly$a`L=2!tRsSewX%5|D(F}wEAylFrC%G&V?}@u7uQ>#Vzg$O}l^lIG4T-QYyIZ$7q#y^ zOT*@6!AoIj?#ubUtjh@V3X7N*)DBJ*UiRBcOZ&fQkKezd-^}njfkmp42MwOS=hwig zkc;LqPWO7XZ0-(I7iqY*&6&8T*7g)4h24XfBKajozsfSh%7xDq_n7FOrE~s>?#Hlm z46}&G&65!dg&Vg0M_-lvf&C}NC(Y|2djHynJGt@C)IRuYjF-c7UjkdVeAz?p5+8kL zW=T^o4x$)$J>b zisfs;L=+;=vNrdJ>O#7tofAd!Zz&Euv_7jF?Vzs{9?FvYTXCbDp4GVY3GrMDq$Upn zm7XkjaNPBjb@(@(IY(~QzWr~DF7OsnzET$)QY_I`Z$@sg`~wWv^sBxlqOtpBmWfjM zX4e1~1ds>i5)0vUw6D{3H0j`pGVS2szkf$Eek6SfmnQ_82S01Hn*ZuiDg9|#X)TV< z$Ipa?ha5(6tWB)he<{-&eQNK))wq7dFn?iFkHzYwV^h^QCu5C%=zI%2x-2F6&kqN& zsD#bV4i?A>cL*)A@EsgoUp0gqmvu)mlUls10!F+BZ1T6qXg81bL-!BKge@}YJ$+#u zi!lI6NxB6vZRo|GcgN82P-0yG5igDY=x`-!U~sEvZ-W{N#LBMe8fi};HUfBiUr6AF zK;KEY^+Yp06em;(&aA!+f?EuTDX{Jjzq>c;?BJ?c^JiC><$kZGfaI|Kof`r5x+Sij z+B0jF*VvLqa1lae`BH^i2YILzF)ePq$kiC|TA*Sb$6k#xT;vl;7MrvtOxEX?Cyqqow0vuejFQ#rFp+Ln8x{-7hKj<64R6(E|7zFT>E~r;z6rWKz*xZ*4?puY5~HVhWfiCMJrg zU9Om<(-R|D7S};;O6$F!;b*|fcG}7Uy?%y}6vd{uTjUMIV)O;XDZJ$iudN0`cpz5V z```#Dnz~VA zv$q9p6fs^RT-+%H@5J`iv(*3$4dTXXrs`>x<&6}$kW(2!UK~brMwEm?W}Fuu?2(Vh z#t?3SL#6mko2btDNoR*u2iE;BdUY{O->Zj4x$tVU**>}$*E`&RyQP|?ME94V((u+d z#cQ9JrE8aBn%wyYuFB2Z$~J6SY%^?N3w183n}5&C%vwTQA!bb2!;jRSJtLYuwd6At zx(wo&UFV&I*{#&90$J;NA;*+kbn;M{<2P(1H2lLy0R%A&|ELf4L3Fy3o;t{fVE1C3 z(#=@a*hrHK(T zKV~P_k~Taoa;b{ReHBv~YE&YsP z$|2MZzZnWHmmwCkcyt19g^`gKvMW#ft|o|s@AyBt7)8_K;ZqJG=XW-zwGf)rQ4tGU zE7!eFiS4l?T*p3#PxI~&k!|(ylB&(8nPI1hZ0bb?kSvpb{qB^5v zlWfF!lOZ@VEfjh7Mi}eC}h`SjNM9Gqj>3F6RMO8UnF{dzs{bOwLRKI!;T7mD`Z8urLEVC z(3K;~6D(ry`)V_g{FvT~NFizf9>wjgpA^Q|?Xcb#0LEF~;e+n*$$gXq+`ywIdmqrhP|IeU{CE=zYtvO{x$_X zCU&x~X#?jSXK2#XwH^o2{uc`D^EsQfJ8N$~ngHG`*w;V^3V7!0T^P#-R$c7_m+GJD zHJ8%Z_@U${657o}|HRplTW&!euz9v(SBx|kIQ_oh{<3X*5pHa~v%gA+BXiWBV9NMf zhbs@VO|kb2but$GE!e9(te@Kj7g5$FPw-;ctG-0CHps<=p zP4$jB?grzDw3*y9swnS~Ol@k_&Dzt={Ph=YJuG3i zwf|UqY*N=xyx-?S(mo2%Exp=0kmh%BWEi0W;SgG|Ts)kEAecYs#Z2W$|EjY>~(niNBr zercRI8IZcE-xz z;DT?Wq;LQL?T^kQ3DBY^pOQi#&*czpN#X6;Ijr|+jRTbEmH=e-;y?uwigviS72fx_ zM+&z2qq};2(f89`8|s9pQWtg`0A-Ps5$xDFtu z(rf_w>zk$dcBwLzN#Uwsm*^k@BnbBb4ez)7o6M@?yfO-P>e$IkK9dHWjuW7fQNqG} zAVKm}n0+QZHp%~r)eG}m3os=>go;FJ%ikzj-sESZTg}hgxtlYUOKbXf*F>*kD`b#`BiPb+%FD<_9z_h3)liTW}_OUFue+p|%?Hi90aS;67qKx+2 z|25l8a?_Yy`%+4Xxw$6u8@bS}pKYhRqMBtERr`#(oEBH7$s3Jtt-3>H-HvImJ@U_* zM8{vlC)5K<;pn;ehg#5j1v+8j>L*NOkD5H<%v>v}qQ0|Yvb#0aMy#I+(8kt$azDO` zbWSsR1eULh;eS_jrL_i?zBkym^mcqlLEYQj1_U9JfZ@mei4k>POS$W)__b=# zv>@2rSUovZ^}Y2m8hzhihy@f_xBghK#kGG`PJdqc;z!*<9kxi+?YHP5l4a5ME*&Ti zjl|mtkj|qDQJ?uYaH>eJ$d`AeaHnqsY`KD+Y;2}=BJ`{tee5Q1nYSP&Fnq}ce5z|2 zw9MOTh`0Pnm(QO!R5|p)ddQ7QWP|_P#k)iOb;EnZNV*h}K=%HR%W%$~Qe)r4*&qIt zkLe|R?t$18Fq|~nQvB^n7VTs=u~q;dOCc|f0Br{7uHZBlXVH!BF_j3VXDuohBH+6Y z#&ZKFV80dEnP0x9!FOQ8uxctj9tuCIIR*4!s;szTwsKuWcI4 zScX}0+wEyvv1}j#{2JgL@@52|rChK4BVi2)fcDD6(M1fPFKu{#+RV*hH?dVtQ(7NF z$r-^pBb|m=O&s7C8kjL{`46T6*i$XxoJ$Prz_B^Oc@`^w_6_?Icb4dB+tp&A$5AI9 zW^ET1ce)E`+WZ9e!OR<%baB23*DJM#?ssaX*uZW-mppVY=jUAq0de1*+ufD9*y=f* z!W0sOp@iC6A0xEob3r!@us~%d&q_uTcP7-f4o;IMp@nrOf?l6nL zbprO%-4P3HjC1#He+ZViEs|G!vlPX->(#HCZwVN{Nw#1y|12Voh8c3+1_O}OS?Y1S zXqoC=2N%js4U3!6TpnVRvG~peZ5j6R)+&NQe638~hGgbg~%YE?_;1Lg1|Y3r-(Ijl&U zff2SIox6c8mal**wvS1hvt}^6=o94DpHKVgB|Sox*X%m(_7;Q8C=QF98m4KOlyYBf z$kTrtU}`(`9S06gm3r<6L>}94fv(!FcQZzOmuvQ(q;Xs~v5f4(lGIrixbKj6M~0G+ zLtpz#-~9yG8T2u4N42pR=@eOo~LDQ^1 z>3)b{tqAhEcVrH`ijWfbyNNCDpA~ajmdVnZ8Zgg<@|FI5YbfP<1=BT@vhc&?(%9gP zu6;jQ{r&kk;|`M{_;+-p?e4rSQ=o*fBT>%r$f6T-lw?Nk;FMp>u2sB}q9VfL7$laILDZkCKV2jz{t z`(nvCRVPv7CT*+;7g8|R0RcwJgE-mhw%z)XDP7~2czxcryg%_H3CL%YIUl}=xyIz! zyl9ssqZx$~{3@^39B)*#zKw=s&NCRe!q6r^_3kXZ(2BT=ZBB_~?HuOBO4b0p?{=KIN5=%wPnTBff+`R7he{W#S zzj?om1S2x#60k}m)YYfFk7=D6E{?=#11n=`X7W>j8QM@o} z8VVV4IN8r9L7Rn2Cq25VzvM^{5?AtPaL1DYQiwtvmZBDbE$>-v55_RauKJulc7cuK$5JNj3-X|3Od zdo{dF-1&0nm+cSd#LqfkHe-5YT=wB8-Bw=bx{Yv6mZzV$(ZjvLystqFf^IDS+Dv4u z!J#Zy6fM+cxk|<^)v0E*d$G4^%sbQ7*K1UYdd;7@^i8famuz0vpJC(Ax@p zlt%Q*aKe851dSZ(Jkv@}HVaZLw-X~mRtMP)rs*pnW?PQ?2O>q~wEHJQrc^=bp7Fwm zZ1DQ7R_IlRTQTCgzgWP@`fv=$D+L|8xbVGEj!IVjOvCk4i}aiLt|ZRkrCR$Oa{0+J z3I?N$-_6GgxD9)eD)%}=w+3TeDF^->NQQN`DP@VxdKMn8){cIyM|APCXeSR)#;{^4 zeH^Sk9kt$EZjD&cOSaI*BPCqlklW$Kc7_Y+3nDh!KUzEWIY&sP=wXy`LezC z!Q7`^Hl35gqcaKwtlT0E^RgBs& zmrg2n<;k-4VYNUy^Y2g8I}OYCLB3L) zfwzsOZ?tXtqEjb!{6d4ByTZMXk@79!PGkcBnERo^c`ZbyQXQ%w{{!beDZ^AB&|6M= z+bBXq9PcD*WKV-nCk&6wm_-M4A|SX}ZAuGm*Q-1eev3ab#M2U;mR$hfPxSaYrJB~5 z)=H*o`NA0p3#jtiet>uIPh%lt@bK^u`y`7HKa|ZIxF+nW7Dx-h!Hr>q*4Yd^j6&kg zs=NKTd-X%M@nAl(RY{!$$N~`>ZOU7VTw5VabFLuOVw8^ws-ix~XREur9=g-Z+yIzr z??s}irEG@~(mXJN`62(+SXj`@B!1&Ot)ny%^12w~sWY$thqSkRi@FQ^b%*W-=}x7) zyQKvwDW#=GItCa@I#s$sLh0_75E#0f5r%FUnmy0Cu5-@Yz4vdv|HG`cK6NVF=9BOCsYado%CW^?*_5+NVokEUzCPVgqj^+l&3=qjZ)xujYaY<||TtxN^9s}P-o9C;}x zvF{Uyigu3zJB*8!-+U0yX(puFG1HM$=}sK* ziFm$D7v2F34ysYSm#)mqk)HpRIk`S}YOP2WAKsNx5r+(^EUziH*|LoB zh3?zeyRfR16|10z-T~*&``G;$Ur!YXJiq#$mGe5nZ~vVc zpp-GgI2alj0l#)rfuwwX=9 z3>}LEPcMG4wvpLPMg+b_D3m<@u^Bdp{D4aufaO#Is0-cE^gfog`|!z0$$C414xsCs zkF$++oH@vFLBzI$HF^^MAjMW!YU;e2K?kBCOIvRdIh;-EnQ%6EWdQh*F3wCb!{Z|A zbl+OCL9|$CAr-1sBEKAX1uM||=_%uVQKt07f`$$}G^Y$DimHrU-ClXDBK<4CkC18t zO60n?K+8@c%$$&|?ml^h+8`v zr3f5N9e3Z_wiQn-JM3l>kYon&qLgWrrT2&yJiIZ}sCjl&3g5s6<`Iomn{^QbU~||a zlk+n1YP()h0*Dpy>e2ozx>WdjfMo(wcJN6n%!XF(#0dbWd=KI+%(O%(VMoeA0AS^(4N8cL$Gt)v210LO97O|BvMaT_!)veO|x_B3QHy@maHVY zCx|iHS;kk)`7`r@=ZI$2>izEFDd~-?^XGv;kALUvxpFdSpH&4-{kW$YF}s4+Ouekm z5Nw0jXV1&@7OCYkG}$_UoTe*8j*N)=iN)$#!V%ecZF;qEY7DD)CS$Q=oLX7Zvq+CF-@U%q>uwcHj<47VKLx>}LqHbn= zVtgEJ)glh_I(9ai{Nu^A(@(^TD5Vw*(Op7H$^$n&01jh00d08;Wk*_N%#4>hGS3uk z3kY|mlf`Mi0>O%3AeqbwJlLy+0(*3;rna)Cf?YSt7X*Yv6(5%B|CxyKFszJUgU&sKuA5nKbFL3R4N;Pjd z3wTuHwc_m;1a~bqVnAf&iP|0M@0Zp)Wq&h^hK3-5RsN?`4Kp!2qyI|tf|H*BhfAf1 z!B<9)-LYw-`N-o5{^5tciVz4}nVz?I>VX)J_N&G2M0r^W#{5(S%Pj1GV>k&`LZq!m+OIV) zZ0qZB(e>FzD8ko(Yo_l5MN=bdZVf29Mo{yg8VRkg*Kd4mY5e z;o?pwF?IU=6A;0pu;i}imK&6_ffnf@v493SqB;wZhEM_EjgGVeZvBb^0msOWRpMcn z6$a~K%)&olEV4z)7Xlwxs26NXTAQ{qwvO#U!IRMRE+gV;$%;uDDa z0$CeUDNEiN*J>iYM>wlN#xfA7RDFHjT!D6`1bV!L#<|m5(YIo5=Xb>XmxYS##`^Gv zhWS&Juv64X>&xriMF>)5+?&%b)y>Pv{=0IJkpRshwIv%uQ&8YEp*(FuMhUqzF{}!c zXrU`yQ`5D0sY1c{>+8}7nqFLI^~qfRx$E6-V9@mU*ndoz)))mH+@;-^to~!3?)hZk z+T5^>^q=j>?UaeYPnHiVJN9Zw+2ghsZW{N%vt8G-upl@V#z^{T%PXst#|K?I2cGto z<@Mi>EnTj=K2RWJht})CKKI!r#sxj6CG}?!e-%NR)wuKj^!*@**o?w-ix7M&0sSjo zfntqvTIr;(DN~Qm37gVk7@z`4 zM8W9PHdsTdBEjzcx`ZLDZOrq%@MnooPOdcTlQ8)CFULbyZXbKnDBvHDy!Pc=t+$97 zvr9v1aGHQpRz-O^l4oyJ1`r<{><184Nu-Q$D6qEeyFE`qafq3WbKRPgkO+M3P#l!` z!Slh@*|Wb3Z#C$mF>rLCs;io`MRpUkMz>LE-uJL0p6~$x6U25>-P8=rr`ZDK=y)ao zt!EQofT6Y2kbb{AaboJXSbh9&d6QxE7L8g1lzxr zc;D_f=@Pnd7nNl|^a&gHM?*6)r2nB8R&}Je8>6p5*g@&R3e#iI6lcn?${^e`q%A~! z#%Y_&@o-I4<#TJZSGoFiW_D20=y3Ed_{TQxL3;Q^V%e_rA+5_d;FW^wiC4x0@~wzx z_o2sYz-MX9f_qe|b9;CY-1I)aKO87riAuh|IQ637Wl&T8FeO;CNtnntuwowR@I+%n zA*HG?5l^==BJ-aCL4o!zROh*bVu-t9WE>CV zaNi(?XdDsM`-v=f;{}+?LYhSM_R;bmg$@)@0sge|FvKO;3H59= zjhC|m@`%``E79d+?jkW{ztX|Xn!E}{yL^u1sW&}Y;d%(4meTQl?bML-#0 zhJRyaN2@(AH;7Ak$6IiR&8isM3FXgDG%EhL5=F`N7BI}OMHZ-sr8-#&YKMn&e!bLg zkAmP!OFgl?85-vH4Y@l?%BO!L`T`02`OjREqw+~leSdK3h^|3Fa1ZQJ4}UwGdxV|2 zx%pY_+%#Tfxx=w&c7E-h^sV?|BeB&p0_q?v@xV?WT$uET(6p*1Ty}dM4#STr??99^ z?1;dPdR5{0;(Xi*q^VL-c9ROM9ZXS>lYr$w!q+BAFOa?eA@jqQCX(LTLS-t#Q|R6A zB`fwaxpr}MOdX;NAk{l%@9FIgST?L?xW8Zaa%u?V?Pu(_&kOp6Pl1qPlp}6TRG0q~ z>B!b)ah?|4%D(V86RLoDhfE^~@BWQ^``2opmwe~&K?btO7Jli+J7cqwfNr3Tp%Ok* zDj#rDggAj6!|xDG(lSBpl6KF1fl6xTab61{OK+KL7UxWb@*)4dp}ZU9h8Mu(;~T%@ zMrn7Ytw$f>l&9P@%OuO-HC}{T*Hkj@PgnyZ`JTLoShwc}KZ7sxf-SIPFOrR#8qjP0 zvCih1PT4{IpkF=Q^~?;olmU0Y`PLV8jMr zK3FX`pRO8W5NrBC2fYqZPY|bBuS6|U0+t2xaJOcoEEWd9?GcK1Y>D6)y5nZ&A zN@2ovA1>P(-TGQbO(>HSv*$=bNOF=f?9Q=vS7~fwCoLZPg{xh@dID1?O8{@aB6a*} z$bb{NbGB#R_m^j|I%DToh@rHtSy!&LeM0KgOiXdfW7O<>nr}C$Z}y!llPj(a!FR`M zvG`Agz~9Xnj<88Rb5-(XTy~8~Hoxgyd_q~bLQ*Yr9%cR1x4kS^m;8cy84B5VEs;q< zcDl`Su|4=&$_vS!T9%uSO8G9csovT|)-HDrZs+qZzAnYO=4UEbfu$CwKv$**u~mEP z1;bi`lg;MbRMsstOQ7SeB!{`r*;C6Q$X$qG^CNP{@3}w3K>pf{mCJH zb82j*5~wolr?OcV>JGMJwe7(U{3Jo9&k64(@B<(D?bdWZjR5iBV1D1-?K4jnQO*+f zBX4o6L`|mYeT&*td-TJA$V`$ z*Q44fVO(v?AZE4W+Um&*`NrqoG?b3s>Kqb$*-nPXIU7v{Q~O2#^>e&DA5R{SZtz`@ znK%ZjCq7Q3P^XmT>HMPFpD#Ln;+L*uK!&?3p=tt_@d3*r7MsLdahSkBiNg zKGSkEZwsy1;T`Jhi-!?`R?p+q*>%Susn`G(#k|22UhQ6=)ZXlhQ}5o?0nS@c&z-n{ zlY0SIruf^`32IK(2w+{GGt+p)yR%b`OrOkD?S)uST7*z$PK+PaZbbgiwixU9@CUiZVH^8ZxUSD*pySw|04bp189q8`liO1K8sd(Gx!SOpXGEpRKLnK`4 zq9p1Z4OqlT1uCA|l!7_iYu5iw5wfi!5VWj_u9I`CT3*1{cKv zYdKBY6KR5c>%V#&Iqi2|{f=M5@|Ks86h5YX4kBXG+>ge)7O(YM;2vP{GOo9(igCtV z5M3Mms3l_s?X-JLzdN`_vf6B#R`HQ|SW3*gmrMd2eoOAb0vsM8`IYpXI^vz8Xd766Ir(|DMDy;g#VBCmxn`>hT5l-Cxg?o zloQ_WBh#pOfkh4V=$k`ij|7*!5n~|8fVknex|uNMEKwAt^^$xkq=OB@>xao+#fve) z_J6OH3SjbpgXuq`Q3Dn+YKQtnUV#lAFV7fMIgbc&g=|5F``9H1-0$`u%Z&g$(f!x* z`GU93cGNE7G8jYm>1sxjOs+)#b>Ern%SG&}oad=|d#<5XrbjtOCFNGYT^(zbn2gq? zThHkNVtU~Z4mlDp{Q_)ByYH^vz8s1a3dg{w0Uu|@gL3W698&+kECB34_T?^NKA0dM zis+RqWls}^ZhHMGiU9~4jhD~ScDY*n669o4h8U%hN zN{~gV#1hUE^Lv+CZOqe-v_<0D<=<$a%`go|RM~YWyr>E~D1b5qeXzJFZ5X;|Tn&2i zakFzEs_epFVXe!!l97()9=*QijX=)4;G0${6c=wj(G1{Ts2atpU3Er_a>Be`#=*fQC7yKTtcY<|XEx&SJ_SG6p z0vOfF6EDJl6%!k41A~;>^o2(+%OD(z9GlZ&Byv>I6`+mOhW{TodO7T z*~buZXT!M^2zA*5@1A7Q#|;Gb&(IY);RR9FwvjowAFHC>gWUoBb*mTtV+jJAY7Czr zOBeDHx6!L;FQ7mTB9~W30AXCehkZC0{xp#*-DbCazJ)>G%a5}4=z=JE^7cgQh5XyK z{T;+ba$HK)1Lqq!KuCqh zKJih{!`Bo!3S)}2i$;(fX3 zFswE$_XdnAU=Y(Bj%C@tu+!iR6+Zn}wWv_)_5wbu-#u(R7d)j=afS3pI`_nTKBxYj zk`Gyo9mhsNxDSgQ z5$R14M=1x2Mn6C9EnA;~rUDoVNgcH60ril?r#T@x-?D9XRbqJQFjFrn)<=Ny3Mz*} z06V-t$4rT`Ej?g{-B*kuRGLl*x+>zwD39J6s(4{mV_=>o^Ahv0u4! zLs7sd$+wo8U5jY6RZ+e1^MF>P_yf?#p$o(8l^lTp-|W zt+R=+a_)+R#;zt;60%tqWv))xso1{l8*Es6>-~DqUC23clgYz02TI30qNs}P(ENYb zeB%rKZ{#OlxMLG^lY6G!*t`5B66$a3pw9x2dTsS5aSeMk_8_7$2oKtecu;P z_1#)!1-uBE06Sa9SaoZ7USut9f9nxh$!f=*h>wX?X80UZMV53xi9+BiW@bt~es{SjkO@$t{>xPaQFO3K z7A*Z-Tq&+Eu|h=qMi9fu=GP@spCCTA6%pkQzTUGbHR@xwX09QF$8KWs&(R@`5mXgw zJdwwulXIX9tT}a8q_SwGg(^OY{FZu3%2T!$;wOc?$O>-S9*Vd4)t3HgNJ1zrYE$@dWrHbHQ$kTvr956=7ZGd?+THB zz;-xO+R4lusgZwZShjqwRc_ERY%#$M$V9*De+Set&o3$Fr?Y#vtsPw?D2VD!vfcR6 z{8Hek3=u0*HzasKkes9cD;DVxrs-bxqF~sbuIJez(f|TZ^IY+$+C#kIU|y#CD|e-i z4Zv;tDCZhg0b1mNp^tVunw9Z_cKZSxmGCo52lx6Gy}*5ZKH02l>FBko$PV5 zbxM)L7xz%XHRmgC-IY^JZ~zx#3>7IiKE3~3CQk!Z!~>~wO1-DHXvhF#SCVA%LFHe- z$)ot)(8denOsP)fB_hBOZZGb-USVL58Mod=fRAa15d5+T{c<3v6nZ%m^I2*hqp7Mw zQ~~E47%-#vb7eExijnvuL93!pH(Vx7&Kavp1K00*<{Nz>t9^Hi(8Sl1&%#Xo!B^+( zlfeaC^0S_)op>TBTb`dAyvIMAgxsv+z%aXyWx+S;VRhQyrt93xTPzBCKHI63$oy8> zqEs~QM!Nn`V-z11o(EYwW2k#~bybj9KA-GXQel7(V(u=zr=aEs1x&l@$ihIOq<7 zc8x?`AtJ!KNl8@MUpR3h=;2dsLSL~euxaD z9eBGn1ipNEy~4-leYzem!iB1v7YoX2NIZEt20yk1%gWvR3AmkrTH~I9JAdCtI8ub9 z2Rb132i(s#<-O)>NP8Zn;?fY68pmMk4Dp~c4BAgZy=)A)-Cv;oX!}n5siRW~vX8gl zkZ&|n5xvJr0!K-{7m>yIR=}gUO#S3W_wBZZm|{z@wHG!GI-x|NN;U}syl6X95;+F} zK~LTC3515}x12wbF$B@+iFKP>tY6lp{;-ZZ95NsvD7<*jSYyHT;2-&2MoadKzpR z=i6BBr6(z~Q0LY_Wz$o$AI_lEoGEO8mY~m1tfK)n8-r!&J+Bz4aS6}@P4&6c>8?eV4|+ryL>(pjhB< z(PVs!{EbH6w7myCv6%SSX!bWPWI@cx4!O=-^@s`_D1BhVX)MUMHhCX4p(R5vzF^11 zO=}_650nY^aWb+b>pf{p8*L=wD@VlT-FRFSwh@ z$W2Ji)UL|2#8zGs8=GCeM)^IRgXT)62*$CR#9}cC?epm9QPTF2p`Y)*lHVPF1=Nmo zbfC|3_N&@!uyx77c6glV$%f~KFFdEK^xMiBq{PMA`I{1iAyiIH zK^)f7-1pFB;^g9j6~f9wS>yCj@oL$$V7}s)N&Ug>X;xPKzaK_8tkrzI`7Eo_8Xx$o>a(@3oK~eDvo+IgloNK|2XueYB z!kIyz7?mvQknG_O=xz%09lPC6&B2N{=jPz+iPvM=e3BF~YU-+t!j!pdi`#opHM{R%l=K%~Iz;@X+HVipBWNeuTL3v;mWhc+Dmb`lSZB)Je zKxjj?Q3@ScunewDIxkRk`#m92TRT!#(Ze_>6?)X19NkGqxrZf7GPqImG0cwJe*U(i zf=lk^^o{d4!A1zoqPJfA#}9Q+%?jjGQXa%y&X13A`r*_*$E#J8r`CKo)0PPdpPX+J zM))!5X=Fgg-3a*ByDf6$CoX#jU{uHBUnIY$_=SJBwIuHUhT()pQMy@zqff)beCxah zf}^KPo%&5oGxwzkBp2fN%ONeq=OC5y%>($neH(*ZPP((@I~`tekrir*?EeP{TnA9m`gy>uV{wxoj`@3m+RF808Me{>lK1Ga2p)2niVCbXE}H zN<$aLDX@xEY2kJ*HsRIHb2j3NqZK=D$qv) zahq$g#Q@*4&Eg&AW0-845RgVWgn;GT-_>!2S;?!b*OD^LfRz6P-kr45H#`yQ+OTOh zE#%T+9(p#7u1mN7Bj^sc{5v|jgVP~8sVCC%UI) zdSytcam47)(O1|`A9$nKKoJsLuPw04i1?%jzr=7B%e6#$TgHfJ$h`eD>(? zOc&rpX?mp49TW^3K~x6J-OE5#!VhPy2n6%DXRSt?7EixTo+X+H6& zTrJwN^=K2fspUoyL2BB@J4XEZM$Xy6DJhbs)pob$(5Aeg`wZMdybkzDKphn_(vi`T zMqU9PzhMB$@sDl55iAbN{^5%?n-Ptx2(XJcRp?_H(j^kRmXPpY-($u7vKCJ6y|nMn zF_CVQd!#VGsV{!03~IPGl$22difw}~_*WwwY`A^8Th_$&5?Y06>>3_(td5nfZ$6Sp z6H83tbo?#Cs9w1E=I}>fDB#Vr^jEPpv|6?HOs)f89>JpRM>!i^+C0qEUZQMzc*h|_grHgOASkO)_&mq8-=GtP%@U9fR-h!11?L8j>~7~L{%^e)s)fn-fI_AU-$9g=2&u>cR_JD{7ZX`gu@DdZD({adwsJ`NND4{CgCXo zwRU306VwwWM-p$@CwQi7tu;PaA87rhNh+e1)^{CkWes;I8;5Fjf-IH~R@dr5r=Dkn z>Z!sQ_bTq35fwvedeUvx58Ncjy;@Wl|LBhzWUF*)4Uk{XvUYah2s!4n6!D(GRO0nL z!Q8C^(SgvO(1zSBpfZWJ0*GYkLder=i%<{0Tu!?E`GWFxELy0qOy02_zBkPD(xtTf z-*qe!>~M9=QDe|n;mE}o6*@%}^*SdsUArx2h|==UeTl44Exs(af8lXrytYGJkFTxs z7^j=uKL_f;6bDSyuaS}WuP~p`wtNW`I?b#%dlTmqtEkgOPB^o=+h-6+KMpkv^XCRkzOq^ z=9Hf1mKACYJ+;I70EaP*M!2>ZJE5#ok+9-s^p;_F!5H><*7Ifwz&QQ*T`n3w?08Sd zOKb9wqz$-0vqlj%al5k#dhzPEC$Bd}Ob7-p`{hw&dc-;6 zp4qkUPH#_Ly9B5&Udd9AUOEYWsDn))caw?bQCqSXpvj8mnat#@HMjMWNjeHs zGs$6z#-@yDUk^ikN(Hb;GAar)f6#8485lH#2BHdMD*FTU340jS)EKxJLrhkzK}n70 zgfl)LbehOEx|~jyc4Q2k4Js>7k`tfb@z`rOMcH#;XBk+tuwVj%tXf>FVCb_`wM5xR z%9JgQ>bw;*X~fbw?;^h3-`}77jS#0iGGTH?c6D_{a@H@%u*707 zu`gzPaPC4bZi~)K6N~Nv1rb=f*RwMcGh2PyM1?~G=*@IC!L~bJtx>{&&`J(mwauiO z*P_!rNQ>zhX9>E_yr(xn}^d);oFO}0L1TxN!~Xz zH=mZ5#h%EZ`-a>INC#AfL~E(&plD@0jN5T#jEhg?86gU$uZ*Q|xKOjJ=~li-XvJ$` zYm;o-z6yAvek6Y2NDO@hy+$sE9KmRq?~zO|H^#7a@`|81Rs0z}^lvI4VUwJJ+8~hB zU>xeWk|yTS&2c+nXt_c6kn;`^APQ^a`dVHIEy#(i4hZ|V3&3?lc8HHV0ViYKRDNjJ zt~-}PUb(7N9Cp{d&E~@lsrHT2K+VBoVBhxRA`Rqabm@C>aXCIH+UJBOsb}O<_PVX- zZNQ+?L&?!uON)2fs0rVQlf^ThHF`-CJlVe4&<~QtkmBOvmR0|>gvi~TCqT3#Ztf%n z(*z$Rd7)x*S+nvDKkI(I9U|{)M#4{aOz%M(biTjbPGQ1AS#-D+f<_`hKuFI<`?sT) z#^|(JNzutr{UCX|a2#yl3%X53wcchRVPt}i7_lw}_GA&hG322p`2G7v?Ag8akhV{O z#l>u#*{2D%VZO4o=>BuSr>U)&lRX`gB9%xi*9*2^br zyHIy*?!@U1jN^*1uPvfIZnGI^xJX`7Q?ii}Y#d@@k_I!MlXd00(+2^S85tU(SZtie zZauY-P%IGdJjtP;IP1s)#JCy{4{!Jx?<0d;t+V10eTVE}@^!Cq8G2x@0=jd*bKc{b z`J!Eh3rJnN2_3I_GPg78#gNMqX{tm>I`JH1MSR5&A-<&sSIH$+TZ&U2=b^$bGxY5X zA>=hkZ%n>&o{nseX9*Y()Uwa&h#iqd_~=pmI48Vcq`3bp_R(S{{|Zxn1FLsqk?nOc z={Fj-sgVNWHq;G!rl5*;MsBY=QGLh#wGTY==>-ki1or+2$)4xpQK+mNYjSz|Q zS&;{t)-(O-b#zg=TO_?yf@t@RnRv>o%}b{SAO?HI~2^Z4Bz`&*#a3Oh!CuZ z%|y({fGbIbXVNz&$QwCe{KPPc%hIPhp_UaNO}D`YF)s;Z)oL8TCvB?}(~O#ABt zGC~aOcSLEbX#iumwLp~EZvt!FA?s9ToNE$-0*Yq6hx>0c)u8xw7q4Y|GyXW$1pzkL zaaTsG{&9kH{w2a($+{uIM$eai_dW89s~X4s+IO!M^2%#XZr+VF8Rftf&f;5*j=aN- zN1C*}=lJRzt=Gp_JLTjnS30Kn0T)?X_>VYtNal2&N0PP~>QANnHV5=HY8wN#NAB5& zx^d2R?qc2va$?G8PG@?IucF!}RnKH&;`pC;)B7ka7UBu7;uyNNeYIpK_?LtUPho$I zT~N&_RC%B6Yd0Ss#``bkGVES+o+dru5oS~OEJldytEo{84(T>7@eK;!L#%L5&ufWN zl3!In72qHj2BKI*i4;sE;C|{`WIc@i_Rpt1?e9JDSirO*iwI5AKr3xJ``kyF;q;s@ z$S@~L!jPhLO7i7b_z<{T+%xTLCG*ShfY-WUPV=qT=dH&-&9#A{|AT`c+}LyZ>A$0& z4RQ2$2n~(6JG`Q{pZM|e)yJol3yJn6QGCeEieaNXIQV&eGIg)nCS-#u+bti>PVx#l zKM_zyXe(ft@lONWG9bf`&e>KcNUs`;wixSRRo2V@g$(Hrk>l zn_6OM;MjqtT5{HBEdgzEen|0aM;*)5Z$P%l?(-d7>U2nuXng+Y{O(|RcZXv*rfz=u0+8{npJ#9`%##8 z-SVTqGX$SN)Px6*FZ%uqD#N3o`Gw5Z)}Z!k_;_Mgh6gU+tc{(;EZ=%EoatRMtVSn>GRd6Aw#H0Eho0v0Vi$3~P}H?DpAJxfLU6OIl;%cePt6P!1*k@}caT zyg1M*uP-ND&W~V_g9gWQaUflO<)XEUFXRJ57ySxNuSblKt}h!4ivb!f$EyMF9&D?% zR*~zuN(FCp1N#-m8U+tMwa}*R%R+`vq$K2=U(SbZ)cNS3dV)%5zkbnw&Kg0Poj5;b z!VLa@vjEA~&p$QNb|UG6dwkp8t@}M_MaxO!=dV_zsbNBE60oR=I11N{d1Q2k%t7=C zHx#|6SfiPC*6C(s9lahOPwfB+aU>4ZeZuPMgfzf=4CQnR=7z#5%I33EQkJM5+nj^Q^Knhwg8g1NwMn#OUF69Yvvp9v=f7%pxv3rvsicJ#(aS z+{?{Frc|SVOj3#p-x!eV?rEb(RzglE@sSJhxL^{rBwG_B zLiLql_I~STo1kX5n(>ydwt{Pp^f zNR)R*=Fi>CS<=Xrt3Wezlj^)%lXU3wHKFL;$uN8v--7p)#$Q9kc{M*U$JTbks^c$3 zX5Po1eHF3(S|)1;Pbwdto*>HMJ8}?5b?Js~0dCGWy?kcf6(}Kyb@V4tW-x{CR%~!` z%faa=+~QVGCNd}lz9oH&i|v+CvCB5-qpW~A?2#**P&wBF4?t=!K-z|k1lbyCaoRY~uOBM{s1 z{!fhy|8|}bFO(}X#75%3vTUg@Z%6wCh&GEWy^q&lXNkCkg9$z!V~Jlp5x8&lDG!w< z9Jcgd8dVvWEAU1r-=&145A@7YbWgoFF}}LohgmAz{=`O>aWYsfZ7C+SUY|WbF=@J9 zk_jl-dL+Dk121AkeVpS!RiY^Ro+HC6>bjoN8PJ57I-n+f^jyHuV%f#i$nRS6dBf@S zk8#ldJ}@B=G0zv4uZIuHG~oj(1I)XtLPPiL&Ch;UrsFIW>YUe8U4KJMpN+WOzgZGl zA{VPV-`o(YDoDbEQawW2>`*+RG`XeQ3N(@&h6US%Y_B#YSgoaQ*e&6n^ik~;TjEXh zIIxK(7s!FsjmB|S9NcO{55+%An3aTm-&iCt+ouP3iEEGd*^mH)uQToAv5TvtFZD8Z zrQ^~0BHY#h9VC{~$uE4(*t;ic=Hx!n>t|<s#@q(Av}UYHyK- z=oOdvlKRDTj9KGK=U>E*)^Qr-nSX$qf?7JTu8B`?eL3O!ZO@%XeIF|i@-yM7S-e*d zCn15gIcnd~4vc&+JJy5d{uA>r`}wUq3^1V3k1w=ekk2ba8tYwdP#9p2SPb5F%Og=`>a zyYQEAV=aZP~9^U31(P;L>dtsH?#GAZBHD+*Xw7=;ic8|ce^%Zdy<%insrGCAqe z-Y{utw$o>)*uKR=iENE}p4e18kjE6PXAbWpV@`MnUfD2gtc zt_=v;N?e4%-VSYazA~AZNSJ5A&09ltAzx`Q?(KezgWryD&zSIjToh!@Rl|1?@Ja&!hgnSwbZ-bKWv3kx^; z>-6QV;?W}mqL+#@j>V*<%||r>$$e;~u2D)WYN~^JcB6QTA3ip%WoAZVA53qMSj8y& zVfno32}1pHTNUn<5mNvAw+17&9TuC^usvT9A5!$e!RH7fKm3hI$^tB%v%orlMU`bY z{sdafA^7Xb?Wds(@&)#l6y7SdSI7`};;~s-4B%DZ52X4b&$0acXj$J0QI7~sOrtTD z3VF-DJ|C$__!foDr?=1rF_AC0mzx<^qeQ+5AJ`OPI}+5eCRjw*!-&v zo$@GFXayuZh=rV2OF{AuNl`$<%Oa?1@J;3q%Iob>5iN&VGy~d|wo6>J-j9+-V_#*l zraYW&b1p0zz#~;vLV1olkBMG;i`p*Z6iBAt1s=?y5hCyu9g7G6G9EBmxn#|tNb$ixiaaUQwmIq`nILEFVBH>fh!`%SIa|J%Ip(}#KpxkGo+aX%>$uxzZZ&cKIY7mK7AqHHol?l+dJ}5 zC!AGN(*$#Azi^nV%UiB zI5<2^*6T7IB1j;=f|E){4+Q)fVwZd5q+PIadb!@t&dj6=w6sgMjNrN{~#9_C}_PWQWJI zcG98W--;+XVTKal{##V!4-E|Pfv*3aozt_5LuP3{*Hn0lCoEbDxkngqm0Zefw^vuQ zkQxYI9I0idDUuYF_)UX{|Cq2gv0#S;_3Wv>#N$9qa$qPud7m<`>-= zl)UjQ5jB}*F#i7S=?6kpd7;wCODri_!opw*WxTS}DhKheGk-XPCl|P(+o@h(evO;+2Oy;Q zj~nIzbt}*1api$tq=({+XU(K=C1=dQM!;Nmy6zO&4Lx?@c|OCk9p)_Gpta^Bp#sN` zA9GY`b=Es_v!7poD&xmy>D8Gj)hE@As9-8G*yJX&e#_Zax~au9sCe&pANZVes6gZ4 z;a=yp-oqbIi<0vv^pX<*3--naK38g&JNEi-13t-iXd4>FIWMQPZk_%40dKRlaZn(J zGP9B^=yJwb*OV-=4~>2_6Vs=KSf-k!^qePlY(QyQ!K-{-1mkMQB{v ziKex{=gWB^yy3n?WS(2#%vg*p|PxbbKOJ}YphD3M`>s@HVxBZ5ppOrlPGyp zl|r6(Tvx*w68zzM0&st_ZpD&S;f2T}+vb2pllYyG!FC)-;{a#m6ZTXrAPZG~KwQU- zDOdhEK-|57n))$WPZT52!NYKRp>isMRHz?ln1bTdQO_lw;tk<7&d1B zuZd~sQL&q~zIX0=-dT|=hd21$;ZX%z1sYvrPiXHQeuYillMG#)*AT|bSXtulv-NHsmR_H+(T2iczoMB4jK z!+Bzaj^FLP^SuSK(gxtMxXJnDQI?R7(>?@RO6;5k${xcLT3=J6XJ=5Tu40y>m~g&Q_u6`TqAY9h+NgK16&d zCuq#9H0Y9qSe0i8N;TE^^!*_nFPE$K_v`8zg&J7U5k00${fZ;__l zB8|RLrC9>*JRv9Wa2EbbiQsADueiL6*j?9WlTv*9nRPNLpM%X>7q?~l5_t8H`Drff zZ*8kf3}1=gH&rCy%&OhV9yZ#mQ37o$0GmtoV(JEW*Ad9T0yl0H8VZn#I*>fT@YoRI3cRQi7Y#VYiP zVkpUXi1Uk7QdMrG7^W2p4{rFG?udZ;2ycq~fM8fu`&Y&qehE5;Xuk-+Redo=#wrF< z-C(i3x((BPFCl7*$~`L(q&E09N&bE-aNn^q4Re);;vBNcvAvLvtiEyOMr;WJOyuhl zebOgSy8ivck~+7W5E#S`UWX!-c4eECz~ja+1dx-CyWxp)j5a-Z&J zbh?wAz_+hcQleT?k?F@+R-&NRB8PYt8TH{r1tfy@4a`dE!00S_~u`8;N=2>%YbN=HrXqf z;o%E*Vd|292zXa--ip<^&SlAn-V+p;)9Iw*dA(A#)U-2O^*#pDJy;-((us`3KEoW0 zHn6w%J$_ArUobTXQkb}G6sL)R!1FrV?D*@Jj9QiVn2LGcyzkc4x}dAi{4y6U+PResGQkVD73$TplmCly3cMK~i}o4t2RKH#1yv zg~o)1Hd}t3wCP1b0a&?{KQZ8hN%ZpU#?`MgGKdeN$_Aj8Q;N*s8&itAo}SZM{36!1 zU?1|$_g6cBy?3mD+Zw&NsZ22ySxa^;4%R1$$;xH7ySqjd8SDOnIlq3;FNp=OXTBRu z#-kW0{~iotYQ{s35V?3pAo0%LAH6W?I#XoNqY0Bb5UzR&KGugl1uc}PG4W;Kh9>+t za;5NziH$SzppQFg@rVw2a7Q=u238knkKYVu%2b|NY1^4Bn&AqnD6b35L)OWtdBxsm zr_$#srjht&K9S-NqzTEAmg7**|I@efAmW`UQlZJ{`8y(xvAeV8v_FCh7u%-yEA^C* z*|UOU$@49}*^hr);VW?g)WoBP1yNg@jKPngaW*rve{A>>7=hig@nn6^IHnNQ{r=VaMn zuRdLk%&QmJIdG^$%|MAlmC}tP8EQN`iYqa`Y@{2N#_i!NU_@7a{d_1=nPLJR1WCGS z0sPWg%y#M}U*#(tCJ(fK|1u10_mau^hkNG_+P+d|Npaii*F*y_{U-64pB`=dmX;> zzOhp0_8!Gs%b)VIdyD_X9ESYaWFsPC?c9sx2ypERqt15uh3!jMrfID?wFjSg1`9Ve zZYdOI36s~xv3(@29~RDG5V03R!;^#+-osc)NK)n_Xnrg(-~}`R6aQj<9g0Ud0vfVL zl5-Ta^JK5KHY3w?Ph1j*2A#E+zqtqJ2c9Fq&ccWNgNJNE7LFQx;yyaV31^h!!7Yvk z+1rQ#MFJ+RC$u;_rM5qRFRlt?Nfp-lzsW_L3po6q>)WZ4PdHo(VA5p&p28vi{(T4j zuijuI&{;AO6Q&00!8XWxel>rZ2F~CFewCmR*!o5}MJd5UMNSUPTf-SH)eU{p8;3=l z`9(-V0ylX=%#K9{+uf?gd98D$8#&*-kM0J>eVOd>`D6rL+jgFdMn`Z@O%hJRo#6-n z^;j7CMjmD5L|5oN;0~*d@MXe{MiR!CPvBaiLcfHvv*NQ5#TdeZX-JUX;@ zTyq&P==xgsyQ3Tzq3Euk?4Jj^AvYp*Z!Od~Mmy?0teE|jmzd9!^})!RG$ZHRgszp9 zxhn{+ynpCrU-5mMHFw(+5qQ(eH9nj?2#@eM1>B_s)zg|zO;}I6OLhkAd^SFJYj{Ie z$3c3dfz!}J2hk^XR(mUtvoqh|$U15o;~YZ+R;*)gS&*t)tEa4?^qm1BLy>=!<&=DB znU?OgqUY~}zDqd>=iI^_i4gedkkyR>iGP#Y>4rnqWDvU-y;8v?eb>_hR~=3G!MXwc ztaG>ZD!~!~Gr<##cmn?_vhc;Es~){4c%9OhMiw(8b}8PyKTwb_N34e+-ht)Y_UL`h z8BY$Dr>hfD62vf)M0>QN0^l&G15MgN34w(}@xSRZBpaK9m-+Jo)VhqbMTaZ*-7f|H zb>b)Nw2gj2US}oO>WC>!VJH+wNK<;r^b6wY*C14l4+e$on^VCl+N%YOzX?I zra7RGKU_^qRINlTur?SjgdSiIX~MTUBzW#TCP9=o zl*!h=Jx+@na%G;9#E977k@s207w9N64U+>wd_W;p6^~e~f{wH7XrJR37ggq|n6<@s z2|0Fj9xK$FsRacL*Xgb_du=$w4^I>js{a_B2k({aWSCwHr?d3=C~~iJC&h0ncq%S| zKGn=);U&bA5O2B=W7W2AvA)$Dej+o^OSZ}!Llvn3T5rA_jQ&0M3n>pmSVKC#53b>c}yEe7+fjiKX5Hxy;qSCJi7oklA~@M@KU< zo?51$>r9nrtIVTO#EVq0+cqEG27>C2OCFUfn{9y*6m4CPyxwpJxd;E2P1rj$_x2CU z)I&pPmk>MqO*8%uy4;``4IM|nt-dMHM|Mz~*K0LAheC1cqMI;NyJFt6jdTVD7|MLi zisa?t*%pS~+FgG;1We&Q6$$Wgap_3^=>D+JMRnXKg2Yd=o`~ZFHI+*(pYghhi z=ZnwkV1Uc>FQVu$+fwbMH|)yiB$$6(`&sET-xGTZIx7w+`1>#M^Q{C}EB6njHZ)>; zM7TRI3nFFUk?AngnZH~6kq|MRl;}V7OD&4|oj!+gHR;3}t8}Bw86g%lR}dqUmbOMG z@xlG|#vQ+T(=ZRb(AI{9O_;^*<@&T~v=!|P@!m(|By>hMD5yBD;k4d!$Otq*@s|~p zK?FSC$wdy1n?Exi9~n2&Cu}61MVG5|(B()OL%WvR9Xk@IjhXlqJcz)C?%H8R-e2B! zA(&?rxI&g@wNu0VuC|8+zAMRd*lDgQ45#l?GZ*%F;+W-^B!-kb&8Ji6pCVo}ZI|-r z_oB*fMguzDpZTWEydqXiW~Kf>q#92hc0t{ZN`j$|raAU^9rGXW-bhfXuU)*vKd1_9 z!puy%w)Ki<@|KVI*$eRE)X;F`QzIt&u6459pP>=}6mZWBi)&z~O z>|Aa0(dUms!c?126eoppCojL<6!{YO?(ZQ=`cRxX+vop{-t_5tJ^Bf2FH?K%O*PuT z4g#~IkH^(xl6Pu?6xN$(M7=an_eL#V#iok^zjXUH?dkTTR(o_z%`>^8$tQF_r8;9G zJY(x|oWkbTo1~7`hOV<=ncu!Po7}HyRv7Qm#ZPaw^rB>CgLryh;xd1J1`=|wauquo zdR8jP8>xvVi(vjr`$HXW+MpqF~drS5V`w)dxjr7CJ))hANz7CNNy zaw0Idmm~dG?pl3x8`%m7cIF0?H;8kN1Z0w#Ckyz@KXcJG0xbO4P2b}uujJ9{*E)S3 zVw( zrG+KO1yF#GDFD}_late)x=aCx49np10=Yt}*#ul#vp16RQc}GelIxokPc(J(;_dzo)JMckPRnfdmMHP&CC znRL-E0RUclxj<@#JMHlgjUj7t0N!DqrG zyNR0EupVmY5y?6J3N>pUO`e;i*eC#%z581n0ysHNy;=*g9Ki2IF4<) zNwWM`IUn!Hfw;wB1X9Y`fKOiJ9R=m~*erhzxx&q5+p(yoW3WXYITaN%a`z>6A_qXL z+5O?Q1l8kW(3c>2Ck!*IPWR5O`1^RNKbG>@|CSVO=_P-6oSfc!?%nU2Igv$K8TU{G zgA^fO-^*i_=iyA`8%jyOLj4;40S&E<75QEcn%FwN{2IU6{&VEjgRi>-6yo!{1mkIL z<=rx!HN)CJPmE}I=7c}9o$`8OR$c(cfzX&tsFad83Ar(R_A~#25XNEG(53=~xcjX@Du6Cr4f^hq#ib^; zfCb?Z#n(g4;mJ^@jk^NZe=g71gT}kOXmkFb7r7xUIduN2^?r*VFqXu9Q z##N7y(4cd-VlxNU1NhK>nb&qz@tocFHhF#4rsRnT1`6b=Xa=wBGf8MRjM? zF`gpj_2H8(TK!K?TJQ>al;w zJ#3ZcBN9z%lj>hx>l+$iX_G}fl>#F?iQeO#*wYFx`}p|m1$3cybHEy86Ao)$x12*k zO+hc=aLmjpkxfIArsKpwPEMV?Pv~d~CbDJa{mqOceC3BiyHyJxziC{HJx-liDG;L8 z=ruEfuDdK!vJAPtzdal6!Bl07^n5*I%DEb9&jM2~6QKxm-xpfjkcmsS3JMC3U=0@- z0OH`{P%wQp0aydk)=cYKjI;W zOT-=@4N50QRsx6tvs8uS3VLj^rle%!GTDfi1WDmswphUft<<4qy1Z6v{xO`(LqBD# zZ!gDrrn*~cXa?^ZDvZB7?J>!DXb&w1-F|T7^VrxlJZ!4;0k3!jYxK19n~VRw>5jxV zp0bc8KOZ-(!}9#~M^*jk&hI=4tD%cN6{jaYD@r!;-{7N(WJsY|Z7&K^(GvVqTkiHN zY4U?$9Zaoo5br&1&ZQ#vNPetxo_$P%UZdbumS%iMjeBQ+guE1(uPedY1q#_xlE9Me zvvYKet2E7E%aIt;eDRL!r)a~mKl;7YeDRH3No6biYR%x+r{koD0+#q32{%=F zt$wptF<_aOpHgi^E9YI78m$fy_7s{ec(sIyN#oS`Tff{4NH9sJu6lKI4i1V>el74| zWu?A29kshgLh$jgg$fO<{TnRObD;|7^gRZq(~ za^*!6Pt%UcxP+`(7BraaSU6u8Q4EbN?%@6YwV%RTO1pD*DBki|qa4-VL&`w-mQiT? zuR?Ssw^p*U^b5Pb-&ouu;55lzRQz*D_%csdRah3?BdgHW=95M7n398~HqA{TACH}t z-MXmR-;0T2z2XVcTt=+{S0CaD0}N|E0eLu?cHiHjTi0vyoj%>NNn!~pPGQXgL#D)Q z7>bIVEd22+z=WmW?+U&!Rv3n2K4*i$D93 z#g-K*m}0s`wv|F5ptKRaK>`NGb9E0An1M~K`edxdB8Pvbpxn?WyTeUi#MZp{-T=<@ zcRAFx+tto;mUy?;Mrt-4r}T$iD-m}Brm|p6yTs?3P9ov~mPa@$(+PaB9i&f$n4gao z*{zgFE_gEGxd%rmED$%Z2iJI0dJIoUXv<+rt?eRf({Kgw( zN{2TYv9W?zg)a8fu@qp)V&v9QP%i)^6EM?49kiZDt>6Q+(83(GcuC1y4&P4UP#_e@ z#+C-Miclc5zCiy5_jZk3ZGvHEXj~feCj(-@+sDu#v>@qf*~DJ9i9w*jRn}REv4)w9 z<*OP~LYB3M{(I5UBdP2yVDdb4!~Jm|!4(|qnVe4MdzJm4j0@wbX)nEs57AdDXC00) z_uGEtbuN$HHVe$_cr^$>E)BTjGo-F1rm@fJNOdO)r3I3sV`Ku$^`?dXi#$&hYy~E^ zZuO_E(YO>INRQ9Xpi4SGW8<4hcoyGn(omvf<4<@z0e0dGP0iA76i;&j_1eJl4}|Qsm-x*mzA?rxp7w-`T>D~@I3Gc6^~>VdKwuS{r!Yk(SQmXV#9gE zK2kvgxQ|s^B#ekXVNj+nHWlquR#)d54DNk_(=XBewfxHOqtadKv3v(hUUA`h`Fh|D zn`PxckR4$2Im{ky+}ud)K7N`j5A&5Gs__Qab>Uzf5vs^U7f`Bw@_MzJ@xhK&SLGnJ zjt!T^NLdnxorje`t<1}qAtmp7@g}{CS|$3GC}pC`*~{*PGSd*S;I<8i#YA(bQ-IWY z^?F%~e^I%+VKr?5*8QdT>$(x!9ML{Q6n8(dJyL@ENoH)6HvYh% zfirT6j=;CcSu}98=2lm63hGFh@^Cv{$vqyPcAr(Ng9`falwt(KJfb-`j3HEyeNq}h zw&A^*k8L8JYh?nJkbA*f+VXm2G-9xvtG=O%(d#rhmL(h%yO3 z;S6z*-nh$liP%(d9>NMSH#e`5OW{Lr;i8i16#uB9PxE}+zTAO-dt_6AM{9=}w(>tm z!f(l+7Z#A&d|%hgUlVLPtNvWBn8M2<^ifT&v=oj#8uB_>T)z5(D>q4wMnNc!#HjbS84MW0DD%jd7gu19P>IZwG@-=X4bS~Ok_Q0o0`fG4rXYST&I)BcuY5*cS zYUEFa@y_)r*>5#T#(XsJe^fD^)*GVJ5`4%pn>{5jq~pO?^yu1sW%%b01a!Jr-~5y& z)|XF+8^hME$j-suhAu+k7fUcD*~H_)tNKr$eO?&uC!aH^5)cvfJz0r}7>(h>&U_qg z07hLN> z(2VWDyNG6L1^Sd=zn&5|)jTl5iGBlS2dGn`k?d-0wR}L4)Gg)%MOUSnf9VU&ALI-0 z8gpvu<4yFdZa5ha?(F_lYzeB@UNpPG)+l3RmeM!FqK2?84|ZPH}f zYaT!j+C;btv~I2ULC5O0B{0TKe~#q5TdQ{0u8mt(V8vi{*h^hJx+ zg*;j;Y~do3x>w=JBcnyw;d&gsARml$!}aEXQl-L7^kxkMi>F6*;XSsCMbKdZ6uE7P zL`o!9~yXtx4)Y{0dHtneodO5=%a=WB94-3$(PZlnwS)%A6 z02A~Bsr~CyVcP9a@ycb)%3Ds>pFq zczveSorlByf%yd4ghY4bEjgz8Wawk&_YGuy0bDZi$i@VNsm{o1x(w`AqHXrA=?g)d803krMLjiMr-AXclS_&ERTL4Qn}fxl6KoulZDQv#=4A5eH_v7IsH@6e2bUSN_pPYc$2s8O zs@qvG)~%0UlIfrZ??VettxAG@wr+p_U88G%lZciRSl4ev8FUHzR*!1*of zO{H@5s?w}$gHRyoSVSa9(^lu5zzgAGKOY~e{!She z0|ZcSEe56Wy&u@^5M>p7Bh?n7%WKy zx~iMKcm^6zNJ#iDh$)>t%FMi5e2ez3Gpkryu6X&<;vr#$u1etOLi491aI7c z0(S%lU%>`f0T;EZ%Oix9_fGbEHz_1~{(*FYOPexn$X!f?G`&OK@v)XZV7tx8Dyzno z3qqVtoift^GBwNQl4`L&$p{mZk8@ln=+`KQh_$+(k_IBX*yczozPiariqXdL{Xnnr z2l(+2#;=ys(B@p;T=_)r@}I+bI<-UoppNBsKiYkNeV+7GyuW!{ONZ#a{pjZk9@KHv z`YtB(b%($9JHS5)n?nI24+rthEPF_yS!`=84mI%`@~tq0pcK+3VQ&A6UdlMb^*k0R zhmE6W=$T834VO%6n!Yi_V5(X_>G1joZSXUO{Q`2Fg6dtN(9WZ{JAHt+5c-v3skOft zV2*F{KYTKllr`>hdwuAc)3~_dTP<1~HeGK5AabfkF#s&>t>t;{;?^3_pPadF!s$}d zo3%7)IZl2*d}fMiXz&)*tgiSa8a$ON7i8q|6x+;WvkF%}I(d7t7Z5;IH^th;>N!aU zncQ{s0b#ehPt*J^IhJN;5;CVZ?mUQ@(!XP%XuO_1rJ zldr5gVlfV%Ac(Ip!k8G^wTZs67FOium1boOxx=dygpO2HGhl&I6AnHL4eNOuphwKi z#iSa4X<>6CR-~~RkN6{I)Ci_sa!wQ0T)t6RG+^Xiua#gtE)zy!28P|K766bAnu?k; zVsYAk3PS~49vDHs)zv*Q-De!`%YFwS#wDLZI1rFyOUN5<(1X?lX*rJbvr>LQbUZyj zLJ2xP97culV=d#A&&fgym^|#I+M!}C8xL~FP zpdIV^*<9BH;fD~e>aK_EN{bFNi8WH{?@EIzvtsqZ{xawj`rsg$kXGV_T(=1HDf(ZD zwT)Bc&5zeDFRk&Q81((O4lHf#^}guu zzPe*uenDM)IOBwmY?FDin#4q#+)S?ZV7kTx$|It7Er_EW(q(OZMjw^@yP`maq!!rMnY?5n?zw#Ye&no5? zRz})cL(SH%_ontd*X5yCWsx#tvECehJtM8Z=dsDtNO)h;>C40aR=AA$33=jkmY59J z$rcC5{_web?Ex~FINovykQZEY(Ur$V$p5zUlkRW9I&b^&I=ykalnqq-Legdx&|gjZ zgik;6IVt{IM^No30I&(_57Hl2m5Z8(f7}mCIh13Qd1>af|Lt3e&Tk_-^}RZRQ?J*b zQW7F(f!9F|pofi8$;WD!Yx12{2AI=M)HuOp73I z>HDWBD6Ud2ZbRdoT+l#zPuOQ3uffdES&e?<=w5HJ!3VV18_!qkv$OU%6q137ds*Ja z;q{1a66Y0s=)q-t%dw8`R0RsTm!i450T4>_(xXjgn`5`GdkgIPIFkMMiF&WF6nVOD z(8!>yIB)b>$(U@*XY;^AA%l;jm-`7zkJOV0+TakiSySUa4%Ss)3;!^RXkZcvbafS9 zA94X7PTT&OlK}cwb}tuFF2wB%$rsNnBzH0v?yJ~kbE0Q^LSRZuGs$Gt?0f*I$P%5b z0Nj%aQkBiYcSAE7-v>8A4>bYR5@lVS5bb7ha?`QTU8;r+4Gn+qitlFmTQJQj$jLXk zl0VImcL$6BuvmrE2n>7b@L>|DCcEHEKFZUP)o2a6%sxeMa|6F(jr$Pqn_ZpxZJ? ziLIX?dF>3Q7h_sw8P-jqj*~!dxL@^uqqOC^mB5yKA_(>9;Gx)3efA1yWaZ3M(3P1# z5RQ!AsW%vZ`C%*+i8~01#!;l>RH~aMvwNukx$o6=+Z^1gt+NINvu;1eHV6@!$2 zkp{zz0-ZO=CR4fQ*gdI|R|_lGk`HZsc8Er;-vz`tn8;UE9X{#XR_KQAj8deg0RrL#bziJGmcGC@zSLn-eblrQ>t ztnToEjX~!h^Sd^)<&j9L>DJ5D|HU;u3!KdO&o_NjgZ54T;c{`T#13|$ysqj-!Jo-* z;vz))46(%3Rc-oOk5adxucJ9x?Ish>m+g`c(_4E|apgJ44z_o#@9Ec&Ouo7&@}N)B zA`NXhd|5kjv=S|T`8j7i zUaKXXW<4;B>%a27m}vJAQoFp@iO%1#d_}WP-z-YmLRIE$SMhzZrrW3eHdNsXru72ZYP%Py1@{aHy*3NB_0mFznv+ufL&0f^c;+Sc6#UI>Rwu- zz-=XRw zf4SSZf+3sSrU{YW!$ddSb>XC5XrLie-cRtPD1^K|$sh0%qGXfv%>TSBOef+3$z%p$ zWGwAQm%~wHqlMsysVSy@;6yoL9Ux>Ifxws$Fp_+{_Q0JODmmbG@0^Yy=cr@+5AmkP zS0m{DkVd$_WKu0iCH!>LDt}u@HvFO(I}oU`SK>AEaXBR%p^NQ!Q)#-WBFY=XU+OBP`Y3!woCm54rI^4QYay?!rwy(xm<9WqMjpQ$c#C4CNBP~BfF#ION`>#qI%g+CA-qUm;H%P z|6*;fDrlJ1sgN=yih^J+AfX*lo-9uBpu>*(tCw+J;y*V}gHlcO_&jJ#xt0WhQgp&$ zl1-MadyFp_*Jtt41Asu_1`TO?gI20>7k{na(y5l63UQ3zD~jy_*AF7%jp*6c>@XNf z`Ot>HV79chM1o|&ydL-n+g|%TXAxrPQkvEKt7H&TZ^YW?ELK-Zw(8>KG!5^>YLd-l zyv|^`uKi$Q{xEUOR|U)RoX;F1gF@ z0AVF|ywfz1lJJQI?pg->Q45JO?Npgl8mOmd#DA%dJkzaZS{STpHic%;~1$x=Kp(Jwy*;W~=a5;j{MgIKMh zresAaB?J%LBtIz}Xr9}}2DsT$Jl0rR?J&1aj03m$Jj(6zeBod@;Yce>}_R8bRSxF*mq_)(-n+f!;qf zdL)6HxzU&B=S7tghQlxxr+5TjmwnZ_}BdR`AyrPgg5*{$5f?AK2fw2XZ^+` zqwed}DihO~TbC@)knSf(Y5SEue+**M>>D@EP7fQViO-n8aPkR49KZ{qNc*==wGd|J zg5z+cSa8t^A+-TqKFcM`{ozlA1mKFe$YS;d<;?(>$}7oPRpo|4+QaqH&-*+?&Hm$> zWk`kYyE#KXm<o_XMs_w&b;k$2Gvs;_>q44f z&*1;_0^m3>)Dp%#OT3Jmr^J1ZugtV7OqJ<<#QIkt0DC{l>DgU55Ffp~X|t^-3#R?0 zj+`JUJ@>g7f8wis-!A#a%k{Hl##Oe!w0usglvBh}YN5qu;E*HU(DaiSQH$2V=R@Ri zV{~DE35T!}v56VnbDPUqMCf$ENTis#^mEBYp+Rq}+`WE9#vV^D2=Z*~mRV_<*gApo zpR(o78dpQFNxYMxPmTEJ&`chDC+C3y?3b@!N#;oS?Rip?P8fseJ8j9gyq_0`B6a_7 zcDcZd(5jG(P+mw#Cl0^GQRTyhb!U2 z!c&}~tP#kx?I|?CIQC_0e>cim=gPX?(_yk=tytH>>}63HTLX6xI;;7hRq=tyy;G>H zKDqKV{a;sN$=G)On=xsZfcF+h@J+<;-TYR{Qj516ci6&TI}-jk{0Y9*fB2JBv^5IY z@XB1pDU@m+*u^fn77oAY103TioMc8ITVhG2xxLf)VdSooa|BJxZ<7)h0JI>96D$d2 z;UkCEc!}Elw|Y%~3_15S&ZMa+$aw4D=Yzb^w>CHz)-V?M1bGrX>F&DkUe{R_z8t-B zqt1ZVNyft&P-BlR?Dj(`Ki^ZkcXEQ)&9TJyqfFXj$rq&w0>!ID1ii2Z3Fl+b=BYgF@jIew3i?m2Cd5vIBLR=i< zT5(npCK8Av5-&8yQEz_A1cwkzL0RiS|2Y*M?I7%d$|q5JydsXIDxjeNvTmy=|KP_J zpS8_S?=vEC3z2#n7j=wqB;skek>QPW58kZ&l9_!*Y)KD^s{IQ;L3e_U+g zd=E7ipp^|Bg}zkagXN>SOMABtP$s}O?7W%fPZg8QPhm>Cj${2yZ{5zU^_~y z8^U|7I!yU#0F>noRE-d#&NL$`ss&&N9Mk>SihOk7DR3J!yh0+~A0{&qc!2BrDCE8M z-oHaw$n8SoDM4FR`ih_2ka&|+3#^kDQYijG<>TGQ+e#OC%jt1kIHEpZDp#ItDmqgc z5<;+6ksL}p+z_7$1$bC+ZLWaqt_f5t$s$ZXVAbxc>q>04>fvEi%Z6q;vPiySe#;OJ z=)jc@U=x>pX*12j&PPu(Y#{U7B;bwNo*N71E!UA056Q0$Qc4mYTiExZWT98KF7q6G z&y2$Qw4uk=lbI0%>>BFiHg?_LFt)HxqwNE|OjJ`S{(Rhrq+?hlBoFs1#cG*zd$0>= z+U@#NOELI_yLY>-iJo|KfLp`{0I6P(k;U@ld$A>qo!TW^R7(aFBFi1$D@$z`| zdCJZK`^0x$gJ5yzW}-Kc$&E z2Yhdklk#ftW#;3%BYM-O4+&xW5r5q<$vbhGz9h5SBJSE{;Q6ZCXP+xb$>@vMB}LZA zVjMWE<6e6>k4G?3i%?~(wI-mH+MPq2DQ#T^>gF%cpD>5zvM*c*r?IgO?$d?ZvLm46 zT-Se#yY^8_O>TeU2iRekAR&X{1ovR3)hfKKA#Ol--6=@TGA1d>P%&2LQ#cYcELfTP zIKaOZpzXu^O1HL347P$dOg7jSxFal_X&F^uEZcpHW7-*CWc>{S+{cE=!dlcjw|BSq zn$>+D?oc1!W9o5k{KJCfeQD+tIXZg+EWpF_cvqn-hf={F5#LzR)Msp`LI}cR@(2{e zUQ+;-q0sp+p1jcgZ@%3Zv^zZU3eu$K%RuIzVPP_A3cCaUwD1WL2F|@y83j-ODse*; zH;NfUo?$RYX!5+$ZzTwG`~Gf&t}`Vyj4$5Jz067*c8RG}&JDg8Vl@|@#cS?dPo8VN zEWlJP@cngpbBilf?W){W*8N1e$%2Z;ezyePFpgprahjW%DWAfUx3w*PyTn>{Mct4D zgp;Lt=~x)g72IbY|Fqv%{emx2?0MMtti|J=V|U);ZxbyWznJORxui7?dgt~s-+d&Y zl%K14%$r-y=-nvf=s~!xCNKruPTeHt>ARGbG)DifN4$jxpPm=O z%_hYpXW2LITH;ExB%S$us zKPU$-8p?4=_$wXl)17k;T?6M%5+;Fd0K*&vve*+tdFm@%FGzdc47SwqB9;hp%?znt@a;O!&oC^ShD=cYx*E{2PDtlxj3b)K3D))G?Pc3jyIvELo^Xv zi`hqq+j;iAD67x!D*U&%x>i2bmuB19fvhCf?+Uu#<|+%pNMM|{;AYS1U?%a-#Ka;3 zF4HFN=S1z&?FTF2Ek=uMTC_v|NGglrn8zd;l3#GScYK^C zqrgTh;W9hkv&t))3bUrdxUYi1Mv?unH!YI>=-SSntZ*7x)XT!n^HfRohDAeL^D`~%)e%6!#t&2j zc@G-Eu?~ACBS2gLSwng)aLZKV)!e(nWOy4EK};USUKHzhXXZ^`@$A8>9HV-4)-YcX z&f(%uWyRYMb`X2YJ~R{^^V;#{a5T-+g~~Gw(ZjVa-F#0p4(O*nxFA;x#4MP`70L*v zAs{4tLFb&Bl~uTdE=Bx-`1Jg0?qK<#%@iMQ6&e|ZY^hxt_mw8fS7KDtCn9>{RW%Mn zTi`(hB+2*#ncba-M{|f~l@y2)6;^ZDMs-&C^!>HNFo&pyNpk1M?%P*?=~;x_{m-+= z!BWASl1ve}Xi7M5oyV3cf3XE_$k(f@Xkcz#>w1ZjE79AMotM-Bse&qI*?;qq=VxU(@=JNNSI zb%OgtF}It8`S)`Exi!va_xVZi8N5GNXiOwgA4KcN+&yFJXSwJBFi2HDuUAl7tn}$u z-1DJ}4fTL-43c13WeM$KVU=;TZ;_HpVUz{GF3Mnj;8$(GJo(-*`zB)@7Oc398xLN{ zv+rMiexJlWgH<n$o>Iy>vE-%t$p;At3BY%wf$UQ+{aa2GGBbZf8PIMPmF?TUG_RL*8mHHa=Ff4#OCe|hw@nh z?eE3K?>1&|n=ZFCIqbWCIB4+{Nc+a9psS({Kg>lV)0kZ;x58jdBQpebMiFy3P0=h9 z3`AzsBfO6G25#N&wsSYoXmG*DMXr0u<7V#85MxM0^St)R_#|fkm!hju?W`49+M;&? z9Stv@58ug(4ed$$96RRrqIfnQ17YZLKtqd$$=r>=uKAuISD*-5?gv3-WZls zufcc9nSFVD(&2t4Ee9)2nvJe2qK?y!fhGGbL93Rww8+8<(p#$5XtE?JA>v7ewJo|2 z{UYBFf8YRQx1M?KMZ{xFBw@#xo0)~>djPHie&H;VgG)|czkeV0we*|1xC*Zg^TQzi z($ce?)qC70`RSj?zjNhM*oO<`HW)SvE1y+q#YL7pW$rs4)v^qW$mB@C>6~&bvH<5WnOv>20 z4;j!a=bCLSCj^HV#r9qQd(OCh7tc~~{)dheM@VA-=-=tjioa!VHDK=GA(|guwIK%U z!YsmI9GV$rfLafH=Gh*|D@0MJW`N82qV8y6- z=BUbdH0dEWyyD>5GMOOCG*2_*FZD$VA&gD)( z%>j7-A%FPG4|NtiuC{6a)SKf2YON&zdgy($(70&a)huB<%&DfuJ21BwYlSosZa`y7 z@=}kEmDlpcr%$4<`&b|oKFf_EIF?FM+4m&4iVR_?*04W*L=sJ;nGWJZCnq56x2?$u z^&|`Icoy%c`6TGcVswFjQ+_CDXF!i_2d^3IY!Wbim6A&~S7lpp*4gQqbo4!#3qJ8h z{zA#PCUahEn1p~JL>mQcF^mu|dsU%DA3zNxlzH`|U|Hh3YsN?geVxTl3il)oo$(m8 zrJ=z=jP+QeGT1~1N^^K9 zWGZUA69;9xrCt?nxjk(*!?dgEW9(y_7{YS2pFoCNEcf3BCpV;r217= z5m)4hMu-YiaLSEnBM8KyTA+2_l{Sw76Kdz|f!wSJeM+$9#!yJ~lR3p0R01Zo54`}D zZzFcBwX`A`KhD&KD_HS%YeISA0T5O7NCoQquSA|;+xUOCXQt-^Sg-y*{=GG2FjkK` zuHtYX{9Vp(vuEN`Jl%3d^~l?BKM4Sgi{j@#e6~C9xm3$cVB+cbv?Ya{87wY zGT$c;Z=%%go31^dhE;N{;}aV{%qr;xm=ws7PWJnWII{&b8}ai_Sr9!5Y+=Rwoo~>o zf#4#THDDcUt<&xhZ1r2HkdjTaV331-^gh!nh5fGyKjhBHy+fQg$gO#n45^$H4gY_AL zJ8;kugb{c;kdYnv@MVqurMbIYcjMt^Mj$SIl9dP;)WGzCb7ruvyjjb=;H#onf^9%h zP{%7<0%u2;U$sFit=>G(N?-yH#X(#$1x%981uW1^WBn7@mds&RrWMvI^_GOqgOUf( z*d*~!uaF?V4KO#vBeAbVUs<^W_>fDiVx=?aF^!EfmDd=Fy^*+5&hfNZPN)rpTz;EZ zH8J6`Nv5vqqB<0zl3Yu`q>;Fu%n)5w$eHpbh`yx1;Zkc)bbnZ9T7pZpc+|hXJXnZr zT`4Ph|9YFX0?S}f?}}cVyi^rfvENp7f{obA7Emu499uI@( zTkvqLWgX!;E5o;B@blB=P7*STa+k;?Ynj<_6gMLK^tAR_X$jyrWKr~Zvc=W;bv&3m zi|Xt%v~IlSb+hU%#SQOmdMGmCI@NQbJ!hgmkt&Zq_gM7*aQ2peO}_6R@J4ql9fFcd zNR38mm2OEzN?IBQ3~4DTsnHE0NJke3`RPzDe&ma>gf`CwVg~%DmX@Gx%la#Q5@Mg39&JmOEFtD*eM3 zib=z|`$KVo7jz%qj2(KyS2fAP70S^Z!R+B!^-z}E;J&MGo=Ru_mP!hWukJ`%W-VVS zV0JJFOuOg}W52Gk`eRTNV-#yvYyY3U#b7D3CC;*%#q_90K{QlMk7z?*;b183u@&`7 z>kc^#dOtMbE$lWL%F#58!n|!p_5&gQE+wA_pMOjpP$UlXqi`$y3hFce6xQXL`(=&j zGLJe|R3g{OUi7uXfxY0g_RTG;<`*Tv!gskm4@eVJOZGkgdnCiZSLSy7qYS0}0wBvF z`blp6Wm7vv%#m=-V>^28QFn@F>KC^`J2%5B*zDN_^kSZZFZLJeMaGT3XzKdUcs)^_ zZN5$alrHTq*UPIHH!`bGd#61z%9G{qGU&~24L_$Z%x&C5@)+xBpTw#ZNAKrVntW3! z%cr;YhjQ2RkUHpcFI+?SHJyqr|Hr|)YT%Jd_RL@mwC~*uI;@$ynhk36^gY!2I~=fg zG?VL*n)2>bHB0$-j!iiFDo5WUo`8so1GG8mLy9+Mv1D}*M6Ja>}eXulh}_Tcq4NrK5s}701lk?Jxz&yYAO@@ZLhK41|SNprW_$M>GGPp)$VMiTdTzJpFt0>Zwftx@d z=y-7y!OKbv25)$Tq0X*8caOm*39sW{4KuPZyq`?Gi--&<$fN2wPfNEdU2O9uB~ySv z6gM`i;&5r<2%Ayi9mG1Bn$??cxdlQA69d8=%c9SdC}o6K-;=d>U28}f+&P!D)ZZQ z=;`Tsbm6rpN(+A;IJI-Zuw@bUA&dwbn6hGN$8qep(hj-`p$S&AK8 z9Wz(FZo6*>=glMkm?c34yv41`4yMdfa~~5yLdHUe$gBt3Qa;l~Oy*jH435Q89zAWXQ5BwoLk&BVHPj)8L246lX)8M9yVuIK$uTir|1irO3^SGa{NZ+gz?b?3;!Lu0@O;MANUH2yfZA;$2YeMGJ?qkbw zcARLu<$5X?a2zAofJ+hWxA1u1?Q;IlR~|*j^Q5YEMd1rB6_3cZvsYTvQlrylO3nHH zP1J9Q_ftj86&lVNv%})I_QKF=a_+&E%r4T-i@jd0n7{Wuw18AOFWt8mchhi0dNB!; z;zIC56?Shj?y;Om-B!Q0h)yZbFxTm(USfSFxJY-m7S8B4+&;}nlN2TTIgwSHb+(Fm zqgYdi`g&Bhtv)r_-;dVi;g6TpxXTSwljLFDW>@FkJHP3W(*@p4z&B)i&PITb{ z;YA?JchI5m!$vAyYdju4uEIoaZcUw+s+^qK+`6hpG@K|Y{ODqqdmosH4rki;QJbG$ zgMuQBWI1k-|(PkbHJ$&Ji z<<2q(g)^X(Vgj&%;HjcKJ|0q%MT)qO=3mW;S5-7Lgv;MvlU|-(9Ul2sK>Mv{ggLq2 zDKGBrInUJx(i`INr_OR>Us>0-l-%*hMkcT;gtniJb3+KM)!nK5Kl7U0`}>NmkfA;$ z8K}~T`SZE}RjX2!hZtl8ybi9`jd<8-Zbl6tBmkxUJ?#-Ko;9;05_q)TD=-P_t+KiMml{?uI6A7&I9_%ADohC2{ZG*Tq8dhCaN(Mi{1^$FO6&!QP{=t%ld$MOF)oD7w00ICth0? zbJrD>)oEPVDKf0lPkqPRpF5uZ&BO;osZ^`1C#;R>73d>vF_F1YsC4F!rvdGjtV+`D z==(qKB}tL`@ZvAz<>9-#78SXBiG3n65s2ljh8-In9~b&?O_c^%WG2&V^>b?sQP7eXqrm$O_eGZ||svL^7JK z3dhW6&Zk`^psP)%*U4+7m834kI^NX{B_c9o%)J*jo2VWuOCNr_4KLbNQfD|3sX>{z zb&)kmG$uUp3mT&C@ju9Z|70=Zd_;%()GiMw?@?sYGJq&haK5F9)e1FPfy&6EgOa9$ zp>CRpnIu@t#)>j$kwx2YHNSg%0D_G`_3Ub=;oUuFAV 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%0DL8W=j(B$PPwCQpxZo? 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 literal 0 HcmV?d00001 diff --git a/sample_flow_ai_finetune.png b/sample_flow_ai_finetune.png new file mode 100644 index 0000000000000000000000000000000000000000..1a16736919af3344ed9ed4c489224a8ac00d3bb1 GIT binary patch literal 456761 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≻tDkQ%~waULQ|k9vI9t@|reTEF4;%CKw#X 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 zℑ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

    |W>1p6d9eLpdQ}rKS4~DmOEh32|Uu=^=G0) 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� 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