Why can´t I access the request.query? - javascript

this is my first post so bare with me please.
We are currently working on our first Web-Project and have our problems finding the issues within our project. The project we are working on is a simple website for students to see which groups and modules they have joined. One example for a page is the group-overview which display one group with its members and some other data. The content of the member-table should be filled dynamically based on the group you currently selected. We tried to get this information within the function to fill the table by simply calling "request.query" but the received object is empty.
I´ll leave the code below. Sorry in advance if I have missed any necessary information - feel free to contact me.
Client-Side Fetch:
fetch('/getStudentsIntoTable')
.then (response =>
{
console.log(response);
return response.text();
}).then (text =>
{
document.getElementById("tableStudentGroup").innerHTML = text;
})
Server-Side Get-Request:
app.get("/getStudentsIntoTable", (request, response, next) => {
console.log(request.query); <!-- This hands back "{}" -->
let userID = request.session.userId;
let abfrage = "SELECT * FROM User ORDER BY 'Nachname'";
connection.query(abfrage, function(err, result, fields)
{
if (err) response.send("Es konnten keine Daten abgerufen werden.");
if (result != null)
{
var resultString = "";
for (var i = 0; i < result.length; i++)
{
resultString += "<tr><td>" + result[i].User_ID + "</td>" + "<td>" + result[i].Vorname + " " + result[i].Nachname + "</td>" + "<td>" + result[i].E_Mail + "</td></tr>";
}
response.send(resultString);
}
});
});
The URL Looks like this:
"http://localhost:3000/Gruppe/?Gravelshipping++"

The structure of a query string is a ? followed by a series of key=value pairs, separated by & characters (and with the keys and values URL encoded).
For example:
?name1=value1&name2=value2
Your URL doesn't follow that format.

Related

JavaScript failing when parsing XML that has blanks in nodeValues?

I have an application that uses Javascript to parses a XML array that is returned from a Webservice and iterates through it and builds it into a table body. It has been working with no issues until lately.
We had some changes on the database that the Webservice is returning results from in which now there are a few columns that could potentially have blanks or null values.
The Javascript fails to run when it hits a childNode that has a blank or null value.
Below is a snapshot of the browser error:
So my question is how do I handle those blanks so that the Javascript will just build an empty string into the table body and continue iterating through the xml array?
I have tried to build an if statement into the Javascript in the for loop to replace the blank or null value with '', but I'm not sure it's going to be doable with the way my table body is being built.
for (i = 0; i < x.length; i++) {
tbody += "<tr><td class=col1>" +
x[i].getElementsByTagName("CheckInDate")[0].childNodes[0].nodeValue +
"</td><td class=col2>" +
x[i].getElementsByTagName("CheckOutDate")[0].childNodes[0].nodeValue +
"</td><td class=col3>" +
x[i].getElementsByTagName("CheckInOut")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("address")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("names")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("companyName")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("Name")[0].childNodes[0].nodeValue.substr +
"</td><td>" +
x[i].getElementsByTagName("contactPhoneNum")[0].childNodes[0].nodeValue +
"</td ></tr >";
}
Being as how this is an existing application I don't want to rebuild all the functions that build tables using this method so I hope there is an easy solution to this that I'm not seeing.
The problem is that .childNodes will be null if the XML element doesn't have any content (any text nodes). You seem to have a lot of repeating code, to fix this you can create a function to get the content of an XML node with a specific tag name if it has any content or return an empty string.
Here is an example:
function getElementContent(element, tagName) {
const e = element.getElementsByTagName(tagName);
if (e && e.childNodes && e.childNodes.length) {
return e[0].childNodes[0].nodeValue
}
return '';
}
const tbody = x.map(e => `<tr><td class=col1>${getElementContent(e, 'CheckInDate')}</td><td class=col2>${getElementContent(e, 'CheckInDate')}..... `).join('');

How to increment msg.payload[i] in node-red

We are working on an ubuntu server, where we have installed node-red. What we want is to take data from one table on our MySQL Database, and then move it to another table.
Our flow looks like this:
Our 'Select Tolerance' node contains:
msg.topic = "SELECT * FROM Tolerance";
return msg;
Simple code, which selects data from our database. If we connect a debug node like this:
We then see an output looking like this:
We want to pick all data, so we need to go through all objects in the array and make sure to take all values and send them over to our new database table. We're using the 'New Tolerance' node to do so, and 'New Tolerance' contains:
var i;
var secured = 1;
for (i = 0; i < msg.payload.length; i++) {
var time_start = msg.payload[i].time_start
var time_end = msg.payload[i].time_end
var temperatur_lpn = msg.payload[i].temperatur_lpn
var temperatur_rising = msg.payload[i].temperatur_rising
var temperatur_weather = msg.payload[i].temperatur_weather
var temp_compare_WL = msg.payload[i].temp_compare_WL
var temp_compare_WR = msg.payload[i].temp_compare_WR
var out = "INSERT INTO Sunrise_Tolerance (time_start, time_end, temperatur_lpn, temperatur_rising, temperatur_weather, temp_compare_WL, temp_compare_WR, secured)"
out = out + " VALUES ('" + time_start + "','" + time_end + "','" + temperatur_lpn + "','" + temperatur_rising + "','" + temperatur_weather + "','" + temp_compare_WL + "','" + temp_compare_WR + "','" + secured + "');"
msg.topic = out;
return msg;
}
The problem is that we only receive the first row of data (first object in the array) and not the rest. Can anyone figure out why we dont receive all data, but only the first?
The problem comes the fact you have a return statement inside the for loop.
That will exit the function the first time it reaches that point - so your loop never loops.
If you want to send multiple messages you should use node.send(msg); in your for loop. The only thing to watch out for is if you call node.send with the same object multiple times, as the message is passed by reference, you would get some odd side-effects. As you only care about msg.topic in this instance, you can afford to create a new message object each time.
So, instead of the return msg statement, you could do:
for (i ... ) {
var out = "INSERT ...";
...
node.send({topic: out});
}
// Return without any arguments so no further messages are sent.
return;

JavaScript sql result row, using a property that has parenthesis in its name

I am trying to do nested queries in order to insert into a table based apon an earlier select statement. However I am running into trouble because my first select statement selects and AVG() of a row. I have not been able to find a way to get the result row object that I have to select the property 'AVG(row)' instead of the trying to call .AVG() on something. The code is below and any help would be appreciated.
var sql1 = 'SELECT tropename, AVG(criticScore) FROM tropesWithScore where tropeName = '+ '\'' + trope + '\'';
//console.log(sql1)
con.query(sql1, function (err1, result1) {
if (err1) throw err1;
Object.keys(result1).forEach(function(key) {
var row2 = result1[key];
var trope2 = row.tropeName;
console.log(trope2)
var avgScore = row.AVG(criticScore)
console.log(avgScore)
sql = 'INSERT INTO TropesWithAverageScores (tropeName, criticScore) VALUES (' + trope2 + ',' + '\'' + avgScore + ')';
///console.log(sql)
con.query(sql, function (err, result) {
if (err) {}
});
});
});
Fixed it myself,
first, I was getting the attribute on row, not row2 object.
Secondly I simplified it by aliasing my first select state, so it now reads
SELECT tropename, AVG(criticScore) as avgCS FROM tropesWithScore where tropeName = '+ '\'' + trope + '\'';
Hope this helps someone else!

Saving CR/LF in json data

I am saving table data to a json object. The table data is coming from txt inputs and textareas in the table cells.
I'm running into a problem with CR/LF characters in the JSON elements holding the textarea data. The JSON data gets saved to the database fine, but when I pass it back to the jQuery function that populates the table using that data, I get this:
SyntaxError: JSON.parse: bad control character in string literal at line 1 column 67 of the JSON data
var array = JSON.parse(notes),
in the console.
I put the JSON data in Notepad++ with Show All Characters on and the CR/LF was at column 67.
Here's a sample of JSON data that I'm working with:
[["","","",""],["","9/23/14","",""],["","30789 detail, x_vendor_no**CR/LF HERE**
20597 header","",""],["","99 del invalid x_vendor_no","",""],["","30780","",""],["","","",""],["","","",""],["","","",""]]
Is there a way to allow CR/LF in the data?
UPDATE
11684's suggestion to use replace to remove the \r part of the CRLF won't work. Here's why:
Here's the complete function that uses the JSON data:
(Updated to work with Update #2 code below)
function PopulateTaskTableWithNotes(tableID,notesArray) {
// JSON parse removed per answer suggestion
var r, c, note;
for (r = 0; r < notesArray.length; ++r) {
for (c = 0; c < notesArray[r].length; ++c) {
note = notesArray[r][c];
$('#' + tableID + ' tr:nth-child(' + (r + 1) + ') td:nth-child(' + (c + 1) + ')').children(':first-child').val(note);
}
}
}
I still get the error on the line that tries to parse the JSON data. The replace function apparently can't "find" characters within an array element.
UPDATE #2
Here's how I am creating the array:
var siteID = $('#ddlUserSites option:selected').val(),
numRows = $('#' + tableID + ' tr').length,
numCols = $('#' + tableID).find('tr:first th').length,
notesArray = new Array(numRows),
rowNum = 1,
note = '',
colNum;
while (rowNum <= numRows) {
notesArray[rowNum] = new Array(numCols);
// Reset colNum for next row iteration
colNum = 1;
while (colNum <= numCols) {
note = '';
if ($('#' + tableID + ' tr:nth-child(' + rowNum + ') td:nth-child(' + colNum + ')').children(':first-child').is('input,textarea')) {
note = $('#' + tableID + ' tr:nth-child(' + rowNum + ') td:nth-child(' + colNum + ')').children(':first-child').val();
}
notesArray[rowNum][colNum] = note;
//console.log('Note for rowNum ' + rowNum + ', colNum ' + colNum + ': ' + note);
colNum++;
}
// Remove first element in current row array
notesArray[rowNum].shift();
rowNum++;
}
// Remove first element in array
notesArray.shift();
JSON.stringify(notesArray); // Added per an answer here
console.log('Final notesArray: ' + $.toJSON(notesArray));
$.ajax({
data: {saveTaskNotes: 'true', userID:userID, siteID:siteID, taskTable:tableID, notes:notesArray},
success: function(data) {
console.log('Save task notes data: ' + data);
}
});
The "Final notesArray" console output looks fine, but now, with stringify added, the function above (PopulateTaskTableWithNotes) console output shows that it's reading through every character in the array as a separate element!
Maybe this will help too, as far as what's happening to the data between the creating and reading functions: the array is being saved to a single MySQL database field and then retrieved for the PopulateTable function via $.ajax() (on both ends).
Having said that, do I need to look at what I'm doing with/to the array in the PHP code?
UPDATE #3
Here's the PHP function that takes the data in and writes to the MySQL db:
function SaveTaskNotes($userID,$siteID,$taskTable,$notes) {
$notes = json_encode($notes);
$insertUpdateTaskNotesResult = '';
$insertTaskNotes = "INSERT INTO userProgress (userProgressUserID,userProgressSiteID,userProgressNotesTable,userProgressNotes) values ($userID,$siteID,'" . $taskTable . "','" . $notes . "')";
$log->lwrite('$insertTaskNotes: ' . $insertTaskNotes);
$resultInsertTaskNotes = #mysqli_query($dbc,$insertTaskNotes);
if ($resultInsertTaskNotes) {
$insertUpdateTaskNotesResult = 'insertTaskNotesSuccess';
} else {
if (mysqli_error($dbc) != '') {
$log->lwrite('INSERT TASK NOTES: An error occurred while attempting to add the task notes. Query: ' . $insertTaskNotes . ', mysqli_error: ' . mysqli_error($dbc));
}
$insertUpdateTaskNotesResult = 'insertTaskNotesFail';
}
echo $insertUpdateTaskNotesResult;
}
And here's the function that gets the data from the db and sends it to the above $.ajax function:
function GetUserTaskNotes($userID,$siteID,$taskTableID) {
$queryGetUserTaskNotes = "SELECT userProgressNotes FROM userProgress WHERE userProgressUserID = $userID AND userProgressSiteID = $siteID AND userProgressNotesTable = '" . $taskTableID . "'";
$log->lwrite('$queryGetUserTaskNotes: ' . $queryGetUserTaskNotes);
$resultGetUserTaskNotes = #mysqli_query($dbc,$queryGetUserTaskNotes);
if ($resultGetUserTaskNotes) {
$taskNotes = mysqli_fetch_assoc($resultGetUserTaskNotes);
$log->lwrite('Retrieved $taskNotes[\'userProgressNotes\']: ' . $taskNotes['userProgressNotes']);
echo $taskNotes['userProgressNotes'];
} else {
if (mysqli_error($dbc) != '') {
$log->lwrite('GET TASK NOTES: An error occurred while attempting to retrieve the task notes. Query: ' . $queryGetUserTaskNotes . ', mysqli_error: ' . mysqli_error($dbc));
}
echo 'getTaskNotesFail';
}
}
In both the save and get functions the $log output shows that the array never changes (with the above js/php code) and pasting the array in to notepad++ shows that the CR/LF is still there throughout.
Don't use JSON.parse, the data is already JSON and Javascript can work with it.
You only need it when passing a string, imagine JSON.parse() beeing like string2json().
I think this might already be a solution to your problem, I've never had issues with new line characters.
As Luis said, the problem is not your client (Javascript, jQuery), besides the JSON.parse, but the providing site is wrong.
Example for PHP:
<?php
echo json_encode(array("test" => "
x"));
PHP properly escapes the characters:
{"test":"\r\n\r\n\r\nx"}
But the source of your data is providing malformed JSON.
To fix the JSON issue, either use prepared statements or use:
$notes = str_replace('\', '\\', json_encode($notes)); // in SaveTaskNotes
Well, the error is on the input data (showed in question). You can't have an CR or LF inside a literal in a JSON string. What you can have are that chars escaped as \r \n. The problem is on other side, where escaped codes are replaced by actual chars and therefore the full JSON string becomes invalid.

Updating a ScriptProperty to avoid retrieving duplicate Twitter statuses

I was interested in writing a twitter bot to help out some friends at a local ski resort. I found this tutorial from Amit Agarwal which gave me enough to get started (it did take me more than 5 minutes since I did a lot of modifying). I host the script on google docs.
FIRST I think this is javascript (my understanding is that google apps script uses javascript...) and when I have had problems with the code so far, google searches for javascript-such-and-such have been helpful, but if this is not actually javascript, please let me know so I can update the tag accordingly!
I have no prior experience with javascript, so I am pretty happy that it's actually working. But I want to see if I'm doing this right.
The start function initiates the trigger, which kicks off the fetchTweets() function every interval (30 minutes). In order to avoid duplicates (the first errors I encountered) & potentially being flagged as spam, I needed a way to ensure that I was not posting the same tweets over and over again. Within the start() function, the initial since_id value is assigned:
ScriptProperties.setProperty("SINCE_TWITTER_ID", "404251049889759234");
Within the fetchTweet() function, I think I am updating this property with the statement:
ScriptProperties.setProperty("SINCE_TWITTER_ID", lastID + '\n');
Is this a good way to do this? Or is there a better/more reliable way? And if so, how can I be sure it's updating the property? (I can check the log file and it seems to be doing it, so I probably just need to create a permanent text file for the logger).
Any help is greatly appreciated!!
/** A S I M P L E T W I T T E R B O T **/
/** ======================================= **/
/** Written by Amit Agarwal #labnol on 03/08/2013 **/
/** Modified by David Zemens #agnarchy on 11/21/2013 **/
/** Tutorial link: http://www.labnol.org/?p=27902 **/
/** Live demo at http://twitter.com/DearAssistant **/
/** Last updated on 09/07/2013 - Twitter API Fix **/
function start() {
Logger.log("start!" + '\n')
// REPLACE THESE DUMMY VALUES
// https://script.google.com/macros/d/18DGYaa-jbaAK9rEv0HZ2cMcWjFGgkvVcvr6TfksMNbbu2Brk3gZeZ46R/edit
var TWITTER_CONSUMER_KEY = "___REDACTED___";
var TWITTER_CONSUMER_SECRET = "___REDACTED___";
var TWITTER_HANDLE = "___REDACTED___";
var SEARCH_QUERY = "___REDACTED___" + TWITTER_HANDLE;
// Store variables
ScriptProperties.setProperty("TWITTER_CONSUMER_KEY", TWITTER_CONSUMER_KEY);
ScriptProperties.setProperty("TWITTER_CONSUMER_SECRET", TWITTER_CONSUMER_SECRET);
ScriptProperties.setProperty("TWITTER_HANDLE", TWITTER_HANDLE);
ScriptProperties.setProperty("SEARCH_QUERY", SEARCH_QUERY);
ScriptProperties.setProperty("SINCE_TWITTER_ID", "404251049889759234");
// Delete exiting triggers, if any
var triggers = ScriptApp.getScriptTriggers();
for(var i=0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
// Setup trigger to read Tweets every 2 hours
ScriptApp.newTrigger("fetchTweets")
.timeBased()
.everyMinutes(30)
//.everyHours(2)
.create();
}
function oAuth() {
//Authentication
var oauthConfig = UrlFetchApp.addOAuthService("twitter");
oauthConfig.setAccessTokenUrl("https://api.twitter.com/oauth/access_token");
oauthConfig.setRequestTokenUrl("https://api.twitter.com/oauth/request_token");
oauthConfig.setAuthorizationUrl("https://api.twitter.com/oauth/authorize");
oauthConfig.setConsumerKey(ScriptProperties.getProperty("TWITTER_CONSUMER_KEY"));
oauthConfig.setConsumerSecret(ScriptProperties.getProperty("TWITTER_CONSUMER_SECRET"));
}
function fetchTweets() {
oAuth();
// I put this line in to monitor whether the property is getting "stored" so as to avoid
// reading in duplicate tweets.
Logger.log("Getting tweets since " + ScriptProperties.getProperty("SINCE_TWITTER_ID"))
var twitter_handle = ScriptProperties.getProperty("TWITTER_HANDLE");
var search_query = ScriptProperties.getProperty("SEARCH_QUERY")
Logger.log("searching tweets to " + search_query + '\n');
// form the base URL
// restrict to a certain radius ---:
//var search = "https://api.twitter.com/1.1/search/tweets.json?count=5&geocode=42.827934,-83.564306,75mi&include_entities=false&result_type=recent&q=";
// unrestricted radius:
var search = "https://api.twitter.com/1.1/search/tweets.json?count=5&include_entities=false&result_type=recent&q=";
search = search + encodeString(search_query) + "&since_id=" + ScriptProperties.getProperty("SINCE_TWITTER_ID");
var options =
{
"method": "get",
"oAuthServiceName":"twitter",
"oAuthUseToken":"always"
};
try {
var result = UrlFetchApp.fetch(search, options);
var lastID = ScriptProperties.getProperty("SINCE_TWITTER_ID");
if (result.getResponseCode() === 200) {
var data = Utilities.jsonParse(result.getContentText());
if (data) {
var tweets = data.statuses;
//Logger.log(data.statuses);
for (var i=tweets.length-1; i>=0; i--) {
// Make sure this is a NEW tweet
if (tweets[i].id > ScriptProperties.getProperty("SINCE_TWITTER_ID")) {
lastID = (tweets[i].id_str);
var answer = tweets[i].text.replace(new RegExp("\#" + twitter_handle, "ig"), "").replace(twitter_handle, "");
// I find this TRY block may be necessary since a failure to send one of the tweets
// may abort the rest of the loop.
try {
Logger.log("found >> " + tweets[i].text)
Logger.log("converted >> " + answer + '\n');
sendTweet(tweets[i].user.screen_name, tweets[i].id_str, answer.substring(0,140));
// Update the script property to avoid duplicates.
ScriptProperties.setProperty("SINCE_TWITTER_ID", lastID);
Logger.log("sent to #" + tweets[i].user.screen_name + '\n');
} catch (e) {
Logger.log(e.toString() + '\n');
}
}
}
}
}
} catch (e) {
Logger.log(e.toString() + '\n');
}
Logger.log("Last used tweet.id: " + lastID + + "\n")
}
function sendTweet(user, reply_id, tweet) {
var options =
{
"method": "POST",
"oAuthServiceName":"twitter",
"oAuthUseToken":"always"
};
var status = "https://api.twitter.com/1.1/statuses/update.json";
status = status + "?status=" + encodeString("RT #" + user + " " + tweet + " - Thanks\!");
status = status + "&in_reply_to_status_id=" + reply_id;
try {
var result = UrlFetchApp.fetch(status, options);
Logger.log("JSON result = " + result.getContentText() + '\n');
}
catch (e) {
Logger.log(e.toString() + '\n');
}
}
// Thank you +Martin Hawksey - you are awesome
function encodeString (q) {
// Update: 09/06/2013
// Google Apps Script is having issues storing oAuth tokens with the Twitter API 1.1 due to some encoding issues.
// Henc this workaround to remove all the problematic characters from the status message.
var str = q.replace(/\(/g,'{').replace(/\)/g,'}').replace(/\[/g,'{').replace(/\]/g,'}').replace(/\!/g, '|').replace(/\*/g, 'x').replace(/\'/g, '');
return encodeURIComponent(str);
// var str = encodeURIComponent(q);
// str = str.replace(/!/g,'%21');
// str = str.replace(/\*/g,'%2A');
// str = str.replace(/\(/g,'%28');
// str = str.replace(/\)/g,'%29');
// str = str.replace(/'/g,'%27');
// return str;
}
When you use ScriptProperties.setProperty("KEY", "VALUE");, internally Script Properties will overwrite a duplicate key (i.e., if an old Property has the same key, your new one will replace it). So in your case, since you are using the same identifier for the key (SINCE_TWITTER_ID), it will replace any previous Script Property that is that key.
Furthermore, you can view Script Properties via File -> Project properties -> Project properties (tab). Imo Google didn't name that very well. User properties as specific to Google users. Script properties as specific to the Script Project you are working under.
Also, it probably isn't a good idea to include \n in your value when you set the property. That will lead to all sorts of bugs down the road, because you'll have to compare with something like the following:
var valToCompare = "My value\n";
instead of:
var valToCompare = "My value";
because the value in SINCE_TWITTER_ID will actually be "some value\n" after you call your fetchTweet() function.
Of course, one seems more logical I think, unless you really need the line breaks (in which case you should be using them somewhere else, for this application).
Its ok like that thou I dont know why you are adding \n at fhe end. Might confuse other code. You can see script properties in the script's file menu+ properties

Categories