/** * Simulates a connection to a database by making asynchronous requests to a * remote REST service that returns the results in JSON format. * Using this class can perform any operation that can be done with a database, * like open/close a connection or selecion/updating queries. * * Warning! You should set a well defined dababase level privileges to use this * class or you could have a serious security hole in you application becasuse * the user can send any statement to the server. For example: DROP DATABASE */ var Connection = new Class(); module.exports = Connection; var Flag = { NOT_NULL : 1 ,PRI_KEY : 2 ,AI : 512 | 2 | 1 }; var Type = { BOOLEAN : 1 ,INTEGER : 3 ,DOUBLE : 4 ,STRING : 5 ,DATE : 8 ,DATE_TIME : 9 }; Connection.extend({ Flag: Flag ,Type: Type }); Connection.implement({ Extends: Vn.JsonConnection /** * Runs a SQL query on the database. * * @param {String} sql The SQL statement * @param {Function} callback The function to call when operation is done */ ,execSql(sql, callback) { this.send('core/query', {'sql': sql}, this._onExec.bind(this, callback)); } /** * Runs a stmt on the database. * * @param {Sql.Stmt} stmt The statement * @param {Function} callback The function to call when operation is done * @param {Object} params The query params */ ,execStmt(stmt, callback, params) { this.execSql(stmt.render(params), callback); } /** * Runs a query on the database. * * @param {String} query The SQL statement * @param {Function} callback The function to call when operation is done * @param {Object} params The query params */ ,execQuery(query, callback, params) { this.execStmt(new Sql.String({query: query}), callback, params); } /* * Parses a value to date. */ ,valueToDate(value) { return fixTz(new Date(value)); } /* * Called when a query is executed. */ ,_onExec(callback, json, error) { const results = []; if (json) try { if (json && json instanceof Array) for (let i = 0; i < json.length; i++) if (json[i] !== true) { const rows = json[i].data; const columns = json[i].columns; const data = new Array(rows.length); results.push({ data, columns, tables: json[i].tables }); for (let j = 0; j < rows.length; j++) { const row = data[j] = {}; for (let k = 0; k < columns.length; k++) row[columns[k].name] = rows[j][k]; } for (let j = 0; j < columns.length; j++) { let castFunc = null; const col = columns[j]; switch (col.type) { case Type.DATE: case Type.DATE_TIME: case Type.TIMESTAMP: castFunc = this.valueToDate; break; } if (castFunc !== null) { if (col.def != null) col.def = castFunc(col.def); for (let k = 0; k < data.length; k++) if (data[k][col.name] != null) data[k][col.name] = castFunc(data[k][col.name]); } } } else results.push(json[i]); } catch (e) { error = e; } if (callback) callback(new Db.ResultSet(results, error)); } }); // TODO: Read time zone from db configuration var tz = {timeZone: 'Europe/Madrid'}; var isLocal = Intl .DateTimeFormat() .resolvedOptions() .timeZone == tz.timeZone; function fixTz(date) { if (isLocal) return date; var localDate = new Date(date.toLocaleString('en-US', tz)); var hasTime = localDate.getHours() || localDate.getMinutes() || localDate.getSeconds() || localDate.getMilliseconds(); if (!hasTime) { date.setHours(date.getHours() + 12); date.setHours(0, 0, 0, 0); } return date; }