{"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: `<a href=https://${project.hostname}/${role}/confirm-delete?token=${token}>Link</a>` }\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: `<a href=https://${project.hostname}/${role}/confirm-delete?token=${token}>Link</a>` }\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    "}]}
