I made a block in which the request to the specified URL occurs.
Inside this block, I can work with the data from response, but outside this block I can not get this data because of asynchrony.
Is it possible to simulate a synchronous request in blockly or in some other way, save the received data to a global variable?
code of the created block:
Blockly.Blocks['request'] =
'<block type="request">'
+ ' <value name="URL">'
+ ' <shadow type="text">'
+ ' <field name="TEXT">text</field>'
+ ' </shadow>'
+ ' </value>'
+ ' <value name="LOG">'
+ ' </value>'
+ ' <value name="WITH_STATEMENT">'
+ ' </value>'
+ ' <mutation with_statement="false"></mutation>'
+ '</block>';
Blockly.Blocks['request'] = {
init: function() {
this.appendDummyInput('TEXT')
.appendField('request');
this.appendValueInput('URL')
.appendField('URL');
this.appendDummyInput('WITH_STATEMENT')
.appendField('with results')
.appendField(new Blockly.FieldCheckbox('FALSE', function (option) {
var delayInput = (option == true);
this.sourceBlock_.updateShape_(delayInput);
}), 'WITH_STATEMENT');
this.appendDummyInput('LOG')
.appendField('log level')
.appendField(new Blockly.FieldDropdown([
['none', ''],
['info', 'log'],
['debug', 'debug'],
['warning', 'warn'],
['error', 'error']
]), 'LOG');
this.setInputsInline(false);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(230);
this.setTooltip('Request URL');
this.setHelpUrl('https://github.com/request/request');
},
mutationToDom: function() {
var container = document.createElement('mutation');
container.setAttribute('with_statement', this.getFieldValue('WITH_STATEMENT') === 'TRUE');
return container;
},
domToMutation: function(xmlElement) {
this.updateShape_(xmlElement.getAttribute('with_statement') == 'true');
},
updateShape_: function(withStatement) {
// Add or remove a statement Input.
var inputExists = this.getInput('STATEMENT');
if (withStatement) {
if (!inputExists) {
this.appendStatementInput('STATEMENT');
}
} else if (inputExists) {
this.removeInput('STATEMENT');
}
}};
Blockly.JavaScript['request'] = function(block) {
var logLevel = block.getFieldValue('LOG');
var URL = Blockly.JavaScript.valueToCode(block, 'URL', Blockly.JavaScript.ORDER_ATOMIC);
var withStatement = block.getFieldValue('WITH_STATEMENT');
var logText;
if (logLevel) {
logText = 'console.' + logLevel + '("request: " + ' + URL + ');\n'
} else {
logText = '';
}
if (withStatement === 'TRUE') {
var statement = Blockly.JavaScript.statementToCode(block, 'STATEMENT');
if (statement) {
var xmlhttp = "var xmlHttp = new XMLHttpRequest();";
var xmlopen = "xmlHttp.open('POST', " + URL + ", true);";
var xmlheaders = "xmlHttp.setRequestHeader('Content-type', 'application/json');\n" +
"xmlHttp.setRequestHeader('Authorization', 'Bearer psokmCxKjfhk7qHLeYd1');";
var xmlonload = "xmlHttp.onload = function() {\n" +
" console.log('recieved:' + this.response);\n" +
" var response = this.response;\n" +
" var brightness = 'brightness: ' + JSON.parse(this.response).payload.devices[0].brightness;\n" +
" " + statement + "\n" +
"}";
var json = JSON.stringify({
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"inputs": [{
"intent": "action.devices.QUERY",
"payload": {
"devices": [{
"id": "0",
"customData": {
"smartHomeProviderId": "FkldJVJCmDNSaoLkoq0txiz8Byf2Hr"
}
}]
}
}]
});
var xmlsend = "xmlHttp.send('" + json + "');";
var code = xmlhttp + '\n' + xmlopen + '\n' + xmlheaders + '\n' + xmlonload + '\n' + xmlsend;
return code;
} else {
var xmlhttp = "var xmlHttp = new XMLHttpRequest();";
var xmlopen = "xmlHttp.open('POST', " + URL + ", true);";
var xmlheaders = "xmlHttp.setRequestHeader('Content-type', 'application/json');\n" +
"xmlHttp.setRequestHeader('Authorization', 'Bearer psokmCxKjfhk7qHLeYd1');";
var xmlonload = "xmlHttp.onload = function() {\n" +
" console.log('recieved:' + this.response);\n" +
"}";
var json = JSON.stringify({
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"inputs": [{
"intent": "action.devices.QUERY",
"payload": {
"devices": [{
"id": "0",
"customData": {
"smartHomeProviderId": "FkldJVJCmDNSaoLkoq0txiz8Byf2Hr"
}
}]
}
}]
});
var xmlsend = "xmlHttp.send('" + json + "');";
var code = xmlhttp + '\n' + xmlopen + '\n' + xmlheaders + '\n' + xmlonload + '\n' + xmlsend;
return code;
}
} else {
var xmlhttp = "var xmlHttp = new XMLHttpRequest();";
var xmlopen = "xmlHttp.open('POST', " + URL + ", true);";
var xmlheaders = "xmlHttp.setRequestHeader('Content-type', 'application/json');\n" +
"xmlHttp.setRequestHeader('Authorization', 'Bearer psokmCxKjfhk7qHLeYd1');";
var xmlonload = "xmlHttp.onload = function() {\n" +
" console.log('recieved:' + this.response);\n" +
"}";
var json = JSON.stringify({
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"inputs": [{
"intent": "action.devices.QUERY",
"payload": {
"devices": [{
"id": "0",
"customData": {
"smartHomeProviderId": "FkldJVJCmDNSaoLkoq0txiz8Byf2Hr"
}
}]
}
}]
});
var xmlsend = "xmlHttp.send('" + json + "');";
var code = xmlhttp + '\n' + xmlopen + '\n' + xmlheaders + '\n' + xmlonload + '\n' + xmlsend;
return code;
}};
We actually use Promises extensively in our Blockly environment. My suggestion would be to Promisify this and then use a generator function. For example, we use the co library to wrap our generated code, then use yield statements for asynchronous values to make them behave synchronously. For example:
For a block setup similar to this --
The generated code would be something like this (simplified) --
co(function* () {
var getUsername;
// getFieldValue makes an asynchronous call to our database
getUsername = (yield getFieldValue("username", "my user record Id", "users"));
Promise.all(inputPromises)
let inputPromises = [];
inputPromises.push('User name is');
inputPromises.push(getUsername);
yield new Promise(function(resolve, reject) {
Promise.all(inputPromises).then(function(inputResults) {
let [TITLE, MESSAGE] = inputResults;
let activity = "toastMessage";
let LEVEL = "success";
try {
var params = {message: MESSAGE, title: TITLE, level: LEVEL};
interface.notification(params);
return resolve();
} catch(err) {
return reject(err);
}
}).catch(reject);
});
return true;
}
As you may have noticed, though, this isn't always as easy as just sticking a "yield" before the block. Depending on your setup, you may have to get more creative using Promise.all to get values in your block, etc. (We actually wound up editing a bunch of the Blockly core blocks to append 'yield' in front of inputs which had a "promise" type set amongst their output types in order to make this work, but based on your setup, this may be overkill.)
Obviously, you would need to make sure this was either transpiled or run in an environment which supports ES6.
Of course, once you enter an asynchronous setup, there's not really any going back -- co functions themselves return a Promise, so you will need to deal with that appropriately. But in general, we've found this to be a pretty robust solution, and I'm happy to help you figure it out in more detail.
You can have blocks that execute asynchronously without Promises, async functions, or callbacks by using the JS Interpreter (docs, GitHub), conveniently written by the same guy that created Blockly. JS Interpreter is a JavaScript-in-JavaScript implementation. This does mean there needs to be a lot of code to connect the functions/commands of the main JS VM to the interpreter's embedded implementation.
Blockly has a few demonstrations of doing this (src). In particular, you'll want to investigate async-execution.html and the wait block implementation. You can find the wait block running live here.
Conveniently, the interpreter's doc's section on External API happens to use XMLHttpRequest as its example implementation. This should should be a good starting point for your request block's implementation.
Related
I am currently trying to replace the name of a file in the Mid Server after a scheduled export.
The idea here is that the file goes with the name in the format "file_name_datetime" and the customer needs "datetime_file_name" for the file to be correctly read by another system.
My main idea was to rename the file after the export to the correct format, but if there is a way of changing the file name to the required one I could do that also.
I would love to hear from you guys as I have no idea how can I do this.
Thanks in advance.
If anyone is interested in the answer, see below:
Script include:
initialize: function() {
this.filePath = gs.getProperty('directory_path');
this.midServer = gs.getProperty('midserver');
this.authMidServerBase64 = gs.getProperty('authmidserver');
},
nameChange: function(exportSetName) {
var exportGr = new GlideRecord("sys_export_set_run");
exportGr.addEncodedQuery("set.nameSTARTSWITH" + exportSetName);
exportGr.orderByDesc("completed");
exportGr.query();
if (exportGr.next()) {
var attachSysID = exportGr.ecc_agent_attachment.sys_id;
}
var attachGr = new GlideRecord("sys_attachment");
attachGr.addEncodedQuery("table_sys_idSTARTSWITH" + attachSysID);
attachGr.query();
if (attachGr.next()) {
var attachName = attachGr.file_name;
var attachDate = attachName.match((/\d+/));
var newName = attachDate + '_' + exportSetName + '.csv';
}
var jspr = new JavascriptProbe(this.midServer);
jspr.setName('FileNameChange'); // This can be any name
jspr.setJavascript('var ddr = new MidServer_script_include(); res = ddr.execute();');
jspr.addParameter("verbose", "true");
jspr.addParameter("skip_sensor", "true"); // prevent Discovery sensors running for the ECC input
jspr.addParameter("filename", this.filePath + "\\" + attachName);
jspr.addParameter("filePath", this.filePath);
jspr.addParameter("newName", this.filePath + "\\" + newName);
jspr.addParameter("operation", "rename");
return jspr.create();
},
Mid Server Script include:
initialize: function() {
/**
*** Set up the Packages references
**/
this.File = Packages.java.io.File;
this.FileOutputStream = Packages.java.io.FileOutputStream;
this.FileInputStream = Packages.java.io.FileInputStream;
this.Path = Packages.java.nio.file.Path;
this.Paths = Packages.java.nio.file.Paths;
this.Files = Packages.java.nio.file.Files;
this.StandardCopyOption = Packages.java.nio.file.StandardCopyOption;
/**
/* Set up the parameters
**/
this.verbose = probe.getParameter("verbose");
this.filePath = probe.getParameter("filePath");
this.filename = probe.getParameter("filename");
this.operation = probe.getParameter("operation");
this.newName = probe.getParameter("newName");
result = "initialize complete";
},
execute: function() {
if (this.operation == 'rename') {
this.fileRename(this.filename, this.newName);
}
return result;
},
fileRename: function(fileName, newName) {
result+= "\r\n Renaming file.";
this._debug(result);
try {
var res = this._moveFile(fileName, newName);
} catch (e) {
result += "\r\n Erro no renomeamento do ficheiro: " + e;
this._debug(result);
}
},
_moveFile: function(initialPath, targetPath) {
try {
this._debug("Initiating file move function");
var inPath = this.Paths.get(initialPath);
var tgPath = this.Paths.get(targetPath);
var res = this.Files.move(inPath, tgPath, this.StandardCopyOption.REPLACE_EXISTING);
result += "File successfully moved from: " + initialPath + " to: " + targetPath + " \r\n Result: " + res;
this._debug(result);
} catch (e) {
this._debug('Error:' + e);
}
},
_debug: function(m) {
if (this.verbose == "true") {
ms.log("::: Mid Server script include logger ::: " + m);
}
},
https://community.servicenow.com/community?id=community_question&sys_id=a56b38a6db326490fa192183ca961987
I am making a POST request with Axios within a for loop to create leads in mass. The leads get created but the issue that I'm noticing is that even though I get a 200 response, in the network tab under timing, the requests are actually never finished, they just say "stalled".
Below is a screenshot of what I'm talking about
This can become a problem, because if I do other POST requests like this, not all the requests will go through. I think I read somewhere that Google Chrome only allows up to 6 connections open or something like that. If these requests are stalling, I can only assume that those connections are staying open, and that's what's causing this to happen.
So I guess my question is, what's the best way to put this POST request in a for loop without the requests stalling? Should I make it asynchronous, and if so how would I do that with my code?
Here is my server.js
app.get('/create*', (request, response) => {
var baseurl = 'https://OURACC.nocrm.io/api/v2/'
var apikey = 'APIKEY'
var title = "";
var description = "";
var tags = [];
var pathname = request.url; // retrieves the requested pathname (ie, /crm/path)
pathname = pathname.split('?');
pathname = pathname[0].split('/')
var parameters = request.query.params;
var path = pathname[2]; // gets request path for the crm
title = parameters.title
description = parameters.description
tags = parameters.tags
var params = {
title: title,
description: description,
tags: tags
}
if(path === 'leads'){
// console.log("Inside Lead Creation");
axios.post(baseurl + path,
params
,{
headers: {'X-API-KEY': apikey, content_type: "json", accept: "application/json"}
}).then(function(res){
console.log("Lead Created");
}).catch(function(error){
console.log("Error: " + error);
})
}
})
This is where I call it client side
CreateLeads(){
var prospects = this.state.Prospects;
var description = "";
var title = "";
var tags = [];
for(var i = 0; i < prospects.length; i++){
tags = prospects[i].tags;
title = prospects[i].leadName;
description = "Business Name: " + prospects[i].businessName + "\n" +
"Customer Number: " + prospects[i].custNumber + "\n" +
"Corporate Name: " + prospects[i].corpName + "\n" +
"Corporate Contact Name: " + prospects[i].corpContName + "\n" +
"Corporate Phone: " + prospects[i].corpPhone + "\n" +
"Corporate Email: " + prospects[i].corpEmail + "\n" +
"Customer First Name: " + prospects[i].custFirst + "\n" +
"Customer Last Name: " + prospects[i].custLast + "\n" +
"Street: " + prospects[i].street + "\n" +
"City: " + prospects[i].city + "\n" +
"State: " + prospects[i].state + "\n" +
"Zip: " + prospects[i].zip + "\n" +
"Phone 1: " + prospects[i].phone + "\n" +
"Category: " + prospects[i].category;
var data = {
title: title,
description: description,
tags: tags,
}
console.log(data);
$.get('/create/leads', {params: data}).then(response => {
console.log(response);
}).catch(error => {
console.log("Error: " + error);
})
}
}
I am trying to run the following code which loops through some data from a previous step and then calls a POST fetch command and I am getting the vague "'NoneType' object does not support item assignment" error when I test in Zapier. I have contacted them about it and they cannot support my code and cannot provide any better data on what's happening.
Any ideas?
CODE START
var noteEmail;
var noteSubject;
var noteDescription;
function attachNote() {
fetch ('https://xyzdomain.agilecrm.com/dev/api/contacts/email/note/add', options)
.then(function(res) {
return res.json();
})
.then(function(json) {
callback(null, json);
})
.catch(callback);
}
var headers = {
'Accept': 'application/json',
'Content-Type ': 'application/x-www-form-urlencoded'
};
var dataString = 'email=' + noteEmail + '¬e={"subject":"' + noteSubject + '","description":"' + noteDescription + '"}';
var options = {
method: 'POST',
headers: headers,
body: dataString,
auth: {
'user': 'xyz#xyz.com',
'pass': 'password'
}
};
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
}
}
//Get Rep name by Hubspot ID
function getRep(repNumber) {
switch (repNumber) {
case "12345678":
return "Steve Jenkins";
break;
case "90123456":
return "John Jenkins";
break;
default:
return "Dave Jenkins";
break;
}
}
var dataBody = inputData.Body.split(',');
var dataType = inputData.Type.split(',');
var dataEmailSubject = inputData.EmailSubject.split(',');
var dataCreatedAt = inputData.CreatedAt.split(',');
var dataCreatedBy = inputData.CreatedBy.split(',');
var dataMeetingTitle = inputData.MeetingTitle.split(',');
var dataStartTime = inputData.StartTime.split(',');
var dataEmailBody = inputData.EmailBody.split(',');
var dataEngagementId = inputData.EngagementId.split(',');
for (var i = 0; i < dataEngagementId.length; i++) {
switch (dataType[i]) {
case "NOTE":
noteSubject = "HS NOTE: - " + dataCreatedAt[i] + " - " + getRep(dataCreatedBy[i]);
noteDescription = dataBody[i];
attachNote();
break;
case "MEETING":
noteSubject = 'HS MEETING: - ' + dataStartTime[i] + " - " + getRep(dataCreatedBy[i]);
noteDescription = dataMeetingTitle[i];
attachNote();
break;
case "TASK":
noteSubject = 'HS TASK: - ' + dataCreatedAt[i] + " - " + getRep(dataCreatedBy[i]);
noteDescription = dataBody[i];
attachNote();
break;
case "CALL":
noteSubject = 'HS CALL: - ' + dataCreatedAt[i] + " - " + getRep(dataCreatedBy[i]);
noteDescription = dataBody[i];
attachNote();
break;
case "EMAIL":
noteSubject = 'HS EMAIL: - ' + dataCreatedAt[i] + " - " + getRep(dataCreatedBy[i]);
noteDescription = dataEmailSubject[i]; + ' - ' + dataEmailBody[i];
attachNote();
break;
case "INCOMING_EMAIL":
noteSubject = 'HS INCOMING EMAIL: - ' + dataCreatedAt[i] + " - " + getRep(dataCreatedBy[i]);
noteDescription = dataEmailSubject[i]; + ' - ' + dataEmailBody[i];
attachNote();
break;
default:
//Nothing matches do nothing
break;
}
}
David here, from the Zapier Platform team.
Your code is syntactically correct, so you're good to go there. Standard (my preferred js linter; powered by eslint) noted that there's some unreachable code, but that's not a showstopper. In your getRep function, you've got code after return (just break, no big deal) which will never be called. return quits the entire function, so it replaces the break you would normally need.
As for your actual issue, you're re-defining callback when you shouldn't be. There's docs on the issue, but the idea is that that's a function that lambda (where your code is run) defines. Redefining breaks the code runner.
Sorry for the confusion here! I'll see about throwing an error in the parser if you do this (or surfacing a better error).
Separately, I'm not positive this will do what you expect. Namely, you use noteSubject and noteDescription in dataString towards the top of the function, but modify it later. I don't think these changes will be shown in the options object. I haven't run your code though, so if fixing your callback redefinition makes everything work, ignore my suggestions. Also, to no-op the rest of the function, you can return [] per these docs.
Hope this helps. Let me know if you've got any other questions!
How do I concatenate these strings to get value from a variable. I want to avoid eval as so many of you are not keen on its use.
function getLesson() {
var lesson = "lesson" + localStorage.lessonNGS;
document.getElementById("lessonNumber").innerHTML = "Lesson " + (eval(lesson + "." + number));
document.getElementById("lessonTitle").innerHTML = (eval(lesson + "." + title));
document.getElementById("lessonScore").src = (eval(lesson + "." + score));
document.getElementById("mp3").src = (eval(lesson + "." + trackmp3));
document.getElementById("ogg").src = (eval(lesson + "." + trackogg));
document.getElementById("lessonTrack").load();
}
This works but I'm told it will cause me conflicts in some browsers.
Simply remove the eval
// Demo data
localStorage.setItem("lessonNGS",1);
var lesson1 = {
number: "1",
title: "Quarter Notes",
score: "scores/01_quarternotes.jpg",
trackmp3: "tracks/mp3/01_quarternotekeyexercises.mp3",
trackogg: "tracks/ogg/01_quarternotekeyexercises.ogg"
};
function getLesson() {
debugger;
var lesson = window["lesson" + localStorage.lessonNGS];
document.getElementById("lessonNumber").innerHTML = "Lesson " + lesson.number;
document.getElementById("lessonTitle").innerHTML = lesson.title;
document.getElementById("lessonScore").src = lesson.score;
document.getElementById("mp3").src = lesson.trackmp3;
document.getElementById("ogg").src = lesson.trackogg;
document.getElementById("lessonTrack").load();
}
Javascript String.prototype.concat():
str.concat(string2, string3[, ..., stringN])
Example:
var hello = 'Hello, ';
console.log(hello.concat('Kevin', ' have a nice day.'));
I'm new to HTML/javascript and I want to make something that displays Last.FM current playing songs, into a div on html, which displays it in text, I have a code that sends the current song through a chat on www.irccloud.com, and I was wondering If you could change it so that It could get received and put into a DIV on a page, the code is below:
and var r is the completed code, so how would I do something in the div that picks up the source as the link above and then grabs var r from it? If so, how would I do it??
I have tried the following code here
Sorry if I do not make sense.
(function () {
var e = "DeviousRunner";
window.lfmRecentTrack = function (t) {
var n = (new Array).concat(t.recenttracks.track)[0];
var album, spurl;
if (n.album["#text"]) {
album = " (from " + n.album["#text"] + ")";
} else {
album = "";
}
try {
var spotify = new XMLHttpRequest();
spotify.open("GET", "https://ws.spotify.com/search/1/track.json?q=" + encodeURIComponent(n.artist["#text"] + " - " + n.name), false);
spotify.send();
var spotresp = JSON.parse(spotify.responseText);
if (spotresp["tracks"].length > 0) {
//var urisplit = spotresp["tracks"][0]["href"].split(":");
//spurl = " https://open.spotify.com/" + urisplit[1] + "/" + urisplit[2];
spurl = spotresp["tracks"][0]["href"];
} else {
console.log("spotify: couldn't get url");
spurl = "";
}
} catch(e) {
console.log("spotify: " + e.message);
spurl = "";
}
var r = "is listening to " + n.name + " by " + n.artist["#text"] + " " + album + " (" + spurl + ")";
}
var n = document.createElement("script");
n.setAttribute("type", "text/javascript");
n.setAttribute("src", "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=" + e + "&api_key=dd5fb083b94a7196cf696b9d7d11bc63&limit=1&format=json&callback=window.lfmRecentTrack");
document.body.appendChild(n)
})();
I updated your FIDDLE,
by moving this:
var element = document.getElementById("rss");
element.innerHTML = r;
inside the function...
hope this is useful for you