How to convert Ajax to Fetch API in JavaScript? - javascript

So I am using the JavaScript port of RiveScript which uses ajax and of course I don't want to use jQuery anymore. There is only one line ajax and I want to change it to the new Fetch API.
**FYI: You can see the ajax code in line 1795 of the CDN.**
So here's the original code:
return $.ajax({
url: file,
dataType: "text",
success: (function(_this) {
return function(data, textStatus, xhr) {
_this.say("Loading file " + file + " complete.");
_this.parse(file, data, onError);
delete _this._pending[loadCount][file];
if (Object.keys(_this._pending[loadCount]).length === 0) {
if (typeof onSuccess === "function") {
return onSuccess.call(void 0, loadCount);
}
}
};
})(this),
error: (function(_this) {
return function(xhr, textStatus, errorThrown) {
_this.say("Ajax error! " + textStatus + "; " + errorThrown);
if (typeof onError === "function") {
return onError.call(void 0, textStatus, loadCount);
}
};
})(this)
});
and here's what I tried so far using the Fetch API:
return fetch(file, {
dataType: "text"
})
.then(function(_this) {
return function(data, textStatus, xhr) {
_this.say("Loading file " + file + " complete.");
_this.parse(file, data, onError);
delete _this._pending[loadCount][file];
if (Object.keys(_this._pending[loadCount]).length === 0) {
if (typeof onSuccess === "function") {
return onSuccess.call(void 0, loadCount);
}
}
};
})
.catch(function(_this) {
return function(xhr, textStatus, errorThrown) {
_this.say("Ajax error! " + textStatus + "; " + errorThrown);
if (typeof onError === "function") {
return onError.call(void 0, textStatus, loadCount);
}
};
})
The app code:
var bot = new RiveScript();
bot.loadFile("./brain.rive", loading_done, loading_error);
function loading_done (batch_num) {
console.log("Batch #" + batch_num + " has finished loading!");
bot.sortReplies();
var reply = bot.reply("local-user", "Hello, bot!");
console.log("The bot says: " + reply);
}
function loading_error (error) {
console.log("Error when loading files: " + error);
}
Using the Fetch API, I'm not seeing any error now though I'm also not seeing any error or success messages.
Am I missing something here?

The fetch init object doesn’t have a dataType key.
To indicate you want plain text back, add an Accept: text/plain header to the request:
fetch(file, {
headers: {
"Accept": "text/plain"
},
})
And the fetch call returns a promise that resolves with a Response object, and that Response object provides methods that resolve with text, JSON data, or a Blob — which means the basic form for handling the response from your fetch(…) call is like this:
fetch(file, {
headers: {
"Accept": "text/plain"
},
})
.then(response => response.text())
.then(text => {
// Do something with the text
})
So you need to take the existing code in the question and fit it into that form.

Related

Problem receiving Javascript / axios callbacks

I am relatively new to JavaScript and seems to have a problem getting success/error callback functions to work while using Axios.
For example, running the following integration test code using jest (npm test command), I get the output listed below. I am wondering why the message 'my-ping-2 success.' or 'my-ping-3 error: ...' are not being printed on the console. I am trying to make sure that the caller of the inner functions can optionally pass-in callback functions for success and error situations. What am I doing wrong? Thanks in advance!!
Details:
I know that the local API server works fine, it returns HTTP status 200 if I visit URL http://localhost:9090/api/v1/ping and tests via Postman. I have listed the full source code below that can reproduce the problem on my machine (MacOS, nodejs version v12.16.1, npm version 6.13.4).
I am using the generic axios(config) method in the inner function because I am using the same inner function for HTTP get/post calls. I hope that is OK.
jest console output
PASS src/__tests__/01_my.test.js
● Console
console.log src/__tests__/01_my.test.js:14
my-ping-1...
console.log src/__tests__/01_my.test.js:20
my-ping-4 done.
Source code for reproduction of problem
import axios from "axios";
import { isEmpty, merge } from 'lodash';
const baseURL = 'http://localhost:9090/api/v1/';
const headers = {
Accept: 'application/json',
};
const source = axios.CancelToken.source();
test('Test my-appcode', done => {
console.log('my-ping-1...');
fw_get_1('/ping', function(response) {
console.log('my-ping-2 success.');
}, function(error) {
console.log('my-ping-3 error: ' + fw_jsonFormatter(error));
} );
console.log('my-ping-4 done.');
done();
});
function fw_get_1(url, successCallback = null,
errorCallback = null) {
return fw_get_2(url, {}, successCallback, errorCallback);
}
function fw_get_2(url, configs = {},
successCallback = null,
errorCallback = null) {
url = encodeURI(url);
return fw_request_3('get', url, configs, successCallback, errorCallback);
}
function fw_request_3(method, url, configs = {},
successCallback = null,
errorCallback = null) {
let inputCfgs = {
params: configs.params,
data : configs.data,
headers : configs.headers
};
const axiosOptions = merge(
{},
{
method,
url,
baseURL,
headers,
cancelToken: source.token
},
inputCfgs
);
return axios(axiosOptions).then( function(response) {
if (successCallback) {
console.log('fw_request_internal success-1 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
successCallback(response);
} else {
console.log('fw_request_internal success-2 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
}
}).catch(function (error) {
if (errorCallback) {
console.log('Calling input errorCallback method: ' + axiosOptions.method + ' url: ' + axiosOptions.url + fw_jsonFormatter(error));
errorCallback(error);
} else {
console.log('fw_request_internal error-2 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
console.log(fw_jsonFormatter(error));
}
});
}
function fw_jsonFormatter(obj) {
return JSON.stringify(obj, null, 1);
}
For anyone who saw similar problem, it looks like the issue was that the test harness finished running before the callbacks could be called. I added a wait for a few seconds at the end of the test to confirm and now I am able to see all the console logs as expected. See the revised code below. I am writing some JS wrapper functions around REST APIs. Just want to make sure that the users of the wrapper functions can optionally override the callback functions. Please let me know if there is a better way to do this. Thank you!!
New console log
PASS src/__tests__/01_my.test.js (5.722s)
● Console
console.log src/__tests__/01_my.test.js:14
my-ping-1...
console.log src/__tests__/01_my.test.js:20
my-ping-4 done.
console.log src/__tests__/01_my.test.js:61
fw_request_internal success-1 method: get url: /ping
console.log src/__tests__/01_my.test.js:16
my-ping-2 success.
console.log src/__tests__/01_my.test.js:83
Waited 4 seconds
console.log src/__tests__/01_my.test.js:84
Finished test wait
Revised source code with wait at the end of tests.
import axios from "axios";
import {merge } from 'lodash';
const baseURL = 'http://localhost:9090/api/v1/';
const headers = {
Accept: 'application/json',
};
const source = axios.CancelToken.source();
test('Test my-appcode', done => {
console.log('my-ping-1...');
fw_get_1('/ping', function(response) {
console.log('my-ping-2 success.');
}, function(error) {
console.log('my-ping-3 error: ' + fw_jsonFormatter(error));
} );
console.log('my-ping-4 done.');
fw_test_end_wait(done, 'Finished test wait', 4 );
});
function fw_get_1(url, successCallback = null,
errorCallback = null) {
return fw_get_2(url, {}, successCallback, errorCallback);
}
function fw_get_2(url, configs = {},
successCallback = null,
errorCallback = null) {
url = encodeURI(url);
return fw_request_3('get', url, configs, successCallback, errorCallback);
}
function fw_request_3(method, url, configs = {},
successCallback = null,
errorCallback = null) {
let inputCfgs = {
params: configs.params,
data : configs.data,
headers : configs.headers
};
const axiosOptions = merge(
{},
{
method,
url,
baseURL,
headers,
cancelToken: source.token
},
inputCfgs
);
return axios(axiosOptions).then( function(response) {
if (successCallback) {
console.log('fw_request_internal success-1 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
successCallback(response);
} else {
console.log('fw_request_internal success-2 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
}
}).catch(function (error) {
if (errorCallback) {
console.log('Calling input errorCallback method: ' + axiosOptions.method + ' url: ' + axiosOptions.url + fw_jsonFormatter(error));
errorCallback(error);
} else {
console.log('fw_request_internal error-2 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
console.log(fw_jsonFormatter(error));
}
});
}
function fw_jsonFormatter(obj) {
return JSON.stringify(obj, null, 1);
}
function fw_test_end_wait(done, msg, waitSecs) {
setTimeout(() => {
console.log('Waited ' + waitSecs + ' seconds');
console.log(msg);
done();}, waitSecs * 1000);
}

How to retrieve detail of HTTP response error

I've got a node.js application that is making some https requests to a ReST web service.
I want to do something that, on the face of it, appears like it should be simple - retrieve the error message that comes back from the web service.
I can get hold of the status code - i.e. 200, 404 etc but not the detail of the error.
The body of the response looks like this:
{
"type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5",
"title" : "Not Found",
"status": "404",
"detail": "Resource not found: X33003"
}
My code looks like this:
var options = {
"method": "POST",
"hostname": "myhost.com",
"port": null,
"path": "/mypath/",
"headers": {
"content-type": "application/json",
"authorization": basicAuthString,
"cache-control": "no-cache"
}
};
try {
var reqWorkSkill = http.request(options, function(res) {
var chunks = [];
res.on("data", function(chunk) {
chunks.push(chunk);
});
res.on("end", function() {
var body = Buffer.concat(chunks);
var response = JSON.parse(body);
console.log("Detail: " + body.detail); // COMES BACK UNDEFINED
});
res.on("error", function(error) {
console.log("Something went wrong with: " + resourceIdArray[i] + " failed: " + error);
});
if(res.statusCode != 200){
// Do some stuff
}
console.log("res status: " + res.statusCode);
console.log("res text: " + res.statusText); // COMES BACK UNDEFINED
});
reqWorkSkill.write(itemToPost);
reqWorkSkill.end();
}
catch (e) {
console.log(e);
}
It would be useful to be able to present what exactly went wrong - i.e the message: Resource not found: X33003 from the JSON above. How can I get hold of that?
You just had the wrong properties of the objects you were calling. First, you were calling body.detail, but body was the Buffer representation. You need to call the detail property on response. Second, you were trying to get the statusTextproperty of the response, but the correct property is statusMessage. Code ends up like this:
var options = {
"method": "POST",
"hostname": "myhost.com",
"port": null,
"path": "/mypath/",
"headers": {
"content-type": "application/json",
"authorization": basicAuthString,
"cache-control": "no-cache"
}
};
try {
var reqWorkSkill = http.request(options, function(res) {
var chunks = [];
res.on("data", function(chunk) {
chunks.push(chunk);
});
res.on("end", function() {
var body = Buffer.concat(chunks);
var response = JSON.parse(body);
console.log("Detail: " + response.detail); // response, not body
});
res.on("error", function(error) {
console.log("Something went wrong with: " + resourceIdArray[i] + " failed: " + error);
});
if(res.statusCode != 200){
// Do some stuff
}
console.log("res status: " + res.statusCode);
console.log("res text: " + res.statusMessage); // statusMessage, not statusText
});
reqWorkSkill.write(itemToPost);
reqWorkSkill.end();
}
catch (e) {
console.log(e);
}
It's always a good idea to console.log (or the equivalent) the object you're trying to access if you're not getting the correct results, that will show you all the properties of the object.

Regarding the iteration query in node.js with mysql

After the update request is sent, I would like to get a success/fail response.
Regarding the response, I have to receive the one response after all update query is performed.
How to receive the one response?
The following code is my node.js server example.
Thank you!!
$.ajax({
url: "http://127.0.0.1:62590/updatingResourceList",
type: "put",
dataType: "text",
cache: false,
timeout: 30000,
data: JSON.stringify(jsonObject),
contentType: "application/json",
success: function (data) {
alert("Success updating the resource");
}, error: function (xhr, textStatus, errorThrown) {
alert(textStatus + ' : ' + errorThrown);
}
});
=========================================================================
app.put('/updatingResourceList', function (request, response) {
var resultObj = request.body;
var updatedIDList = resultObj['idList'];
// Updating the user request format
var idCounting = 0;
for(var i = 0; i < updatedIDList.length; i++) {
var latest = timestamp();
var resourceName = updatedIDList[i];
var client = dbClient.getDBClient(); // Getting Database information.
client.query('UPDATE testset SET time=? WHERE resourceName=?', [latest, resourceName], function (error, results, fields) {
if (error) { // error
console.log("MySQL : Database resource update error : " + error);
response.status(500).end();
} else { // success
console.log('MySQL : Success updating the resource : ' + resourceName);
response.status(200).end();
}
});
}
});
The problem is that you are sending back a response at each iteration of the loop. If you want a single response, then do it only after the loop. In the loop keep track of the results of the update in an array (key should be the resourceName), and send back the results in one go, perhaps as a json object.
What you need to decide, however, is how to handle if only some of the updates are successful. You either have to return an OK (status code 200), or an internal error at the end.

AngularJS service to send Twilio SMS messages

I've been experimenting with creating an AngularJS service that can be called from the controller and send text messages based on particular events in the application. The implementation is based on this, and works as follows:
Firstly, we have the service:
function BusinessService($http) {
this.twilioSMS = {
sendMessage: function(to, from, body) {
var accountSid = 'xxx';
var authToken = 'xxx';
var testEndpoint = 'https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/SMS/Messages.json';
var liveEndpoint = 'https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/Messages.json';
var data = {
To: to,
From: from,
Body: body
};
$http({
method: 'POST',
url: testEndpoint,
data: data,
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization",
"Basic " + btoa(accountSid + ":" + authToken) // !
);
},
success: function(data) {
console.log("Got response: %o", data);
if (typeof successCallback == 'function')
successCallback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("Request failed: " + textStatus + ", " + errorThrown);
if (typeof failCallback == 'function')
failCallback(jqXHR, textStatus, errorThrown);
}
})
}
}
}
Then setting it up in the controller:
function ConsumerBusinessProfileCtrl($scope, BusinessService) {
$scope.sendMessage = function(to, from, body) {
return BusinessService.twilioSMS.sendMessage(to, from, body)
}
}
And then calling it from the view:
<a ng-click="sendMessage('+12345678901', '+15005550006', 'Hey Jenny! Good luck on the bar exam!')">Send Message</a>
I've tested the jsfiddle example with my accountSid, authToken, and phone numbers and it is working fine. But my implementation fails with a 401 (UNAUTHORIZED) error. A part of me thinks that this is because $http does not support beforeSend or afterSend. But I am not sure? Can anybody here guide me in the right direction?
Changed $http to the following to fix things:
$http({
method: 'POST',
url: testEndpoint,
data: data,
transformRequest: function(obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
headers: {
'Authorization': 'Basic ' + btoa(accountSid + ':' + authToken),
'Content-Type': 'application/x-www-form-urlencoded'
},
}).success(function(response) {
console.log(response);
}).error(function(error) {
console.log(error);
});
}

AJAX call and getting return data in Node

I'm wanting to make an ajax call from the client to the backend. I get a successful call from the success function, however, I can't understand how I get data from the server to return from the client.
currently my error trying to use res.send is:
Error: Can't set headers after they are sent.
AJAX
function getProfessorResults() {
var textData = $('#inputsm').val();
var data = {user:"gopal#gmail.com"};
$.ajax({
url: 'http://localhost:3000',
data: { theme: "somevalue", snippet: { name: "somename", content: "somevalue" } },
method: 'POST',
async: false,
cache: false,
timeout: 5000,
contentType: "application/json",
success: function(data) {
console.log("success");
},
complete: function(data) {
console.log("completed");
},
error: function(jqXHR, textStatus, errorThrown) {
alert('Error connecting to the Node.js server... ' + textStatus + " " + errorThrown);
}
});
}
JS Backend
exports.home = function(req, res) {
function passList(profArray, callback) {
setTimeout(function () {
callback(profArray);
}, 1000);
}
function getProfs(teacher_name, successCallback) {
google.resultsPerPage = 10
var nextCounter = 0
google(teacher_name, function (err, res){
if (err) console.error(err)
var teacher_results = []; //Hold all the teachers returned from the function
for (var i = 0; i < res.links.length; ++i) {
var link = res.links[i];
if (!link.title.includes('Add') || !link.title.includes('RATINGS') || !link.title.includes("Hint")) {
teacher_results.push(link.title);
}//End if for comparisons ||
} //End For
successCallback(teacher_results);
}); //End google function
teacher_results = ['tester1', 'tester2'];
successCallback(teacher_results);
} //End searchForProfessor
getProfs(teacher_name, function(data) {
prof_list = data;
console.log(prof_list);
return true;
});
if (req.method == 'POST'){
console.log("true");
// dataReceived = JSON.parse(req);
// console.log(dataReceived);
var obj = {
tid: 'ryan'
};
res.send(JSON.stringify(obj));
}
res.render('home', {
profs: prof_list,
dataStuff : dataReceived
});
};
In the backend, you should have some route where your AJAX call lands. In there, you can invoke send on your response.
In node.js/express, this would look something like
app.get('/ajaxURL', function (req, res) {
res.send('I want this string to return to the client');
});
To access the data from the frontend, access it in your AJAX callback:
$.ajax({url: '/ajaxURL'}).done(function (data) {
console.log(data);
});
I am not getting the context properly but you can figure out by this example .
Sending data from server
response.send("Your data");
Access this data in your client in success method of AJAX:
success:function(data){console.log(data)};

Categories