Processing replies from the javascript Fetch API - javascript

I'm struggling with dealing with my fetch() POST request. It's working successfully, and I can see the data just fine and work with it - but only within the fetch() call. I want to pass data back to App.js (I keep my fetch() API in its own utility module .js file)... but the timing is off. Based on console log debugging, it looks like the function that contains the fetch is returning to the original call before the fetch fully resolves.
These are the console results. The success/text object is what I return from the N4RecordScan.submit() function which contains my fetch(). Then a few lines later, we see the promise resolve. So my App.js is left hanging with no data.
I'd appreciate any guidance!! I feel like I'm close!
{success: "", text: ""}
Processing final response.
Fetch finished loading: OPTIONS
{argo:gateresponse: {…}, status: "0", statusid: "OK"}
Here'sa snippet from my App.JS which calls and processes the fetch function further down.
processResponse(myResponseObject) {
this.setState({
responseAlerts: this.state.responseAlerts.push(myResponseObject)
});
console.log('Processing final response. '+ myResponseObject.success + ' ' + myResponseObject.text);
}
sendRequest() {
let response = N4RecordScan.submit(this.interpolateRequest(), this.state.server, this.state.endpoint);
this.processResponse(response);
}
Here's the function where my fetch() resides:
export const N4RecordScan = {
submit(data, server, endpoint) {
let headers = new Headers();
let success = '';
let text = '';
headers.append('Content-Type', 'text/xml');
headers.append('SOAPAction', 'basicInvoke');
headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));
let dataPrefix = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:arg="http://www.navis.com/services/argobasicservice"><soapenv:Header/><soapenv:Body><arg:basicInvoke><arg:scopeCoordinateIds>APMT/USLAX/LAX/LAX</arg:scopeCoordinateIds><arg:xmlDoc><![CDATA[';
let dataSuffix = ']]></arg:xmlDoc></arg:basicInvoke></soapenv:Body></soapenv:Envelope>';
data = dataPrefix + data + dataSuffix;
console.log('about to send ' + data);
fetch(server + endpoint, {
body: data,
method: 'POST',
mode: 'cors',
headers: headers,
credentials: 'include'
})
.then(function(response){
return response.text();
/* if (response.status === 200 || response.status === 0) {
// Success!
console.log('Success: ' + response.text());
return {
success: true,
text: response.text()
};
} else {
// Failure!
console.log('Fail: ' + response.statusText);
return {
success: false,
text: response.statusText
};
} */
} )
.then(function(rspText){
// The raw response contains decoded HTML tags... we need to clean that up.
// Remove dashes from the xml responses... the eventual js object wont like them
rspText = rspText.replace(/-/g, "");
// Convert the text response to XML
var parser = new DOMParser;
var dom = parser.parseFromString(
rspText,
'text/html');
var decodedString = dom.body.textContent;
// use the DOMParser browser API to convert text to a Document
var XML = new DOMParser().parseFromString(decodedString, "text/xml");
// and then use #parse to convert it to a JS object
var responseXmlObject = parse(XML);
console.log(responseXmlObject);
success = true;
text = responseXmlObject.messages;
alert(responseXmlObject.messages.messagedetail);
})
.catch(function(error) {
// Networking Failure!
console.log('NetworkFail: ' + error);
success = false;
text = error;
});
//.done();
console.log({
success: success,
text: text
});
return {
success: success,
text: text
};
}
};

The problem is you are mixing async and sync operations, you should be doing
processResponse(myResponseObject) {
this.setState({
responseAlerts: this.state.responseAlerts.push(myResponseObject)
});
console.log('Processing final response. '+ myResponseObject.success + ' ' + myResponseObject.text);
}
sendRequest() {
N4RecordScan.submit(this.interpolateRequest(), this.state.server, this.state.endpoint)
.then(function (response){
this.processResponse(response);
})
}
,
export const N4RecordScan = {
submit(data, server, endpoint) {
let headers = new Headers();
let success = '';
let text = '';
headers.append('Content-Type', 'text/xml');
headers.append('SOAPAction', 'basicInvoke');
headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));
let dataPrefix = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:arg="http://www.navis.com/services/argobasicservice"><soapenv:Header/><soapenv:Body><arg:basicInvoke><arg:scopeCoordinateIds>APMT/USLAX/LAX/LAX</arg:scopeCoordinateIds><arg:xmlDoc><![CDATA[';
let dataSuffix = ']]></arg:xmlDoc></arg:basicInvoke></soapenv:Body></soapenv:Envelope>';
data = dataPrefix + data + dataSuffix;
console.log('about to send ' + data);
return fetch(server + endpoint, {
body: data,
method: 'POST',
mode: 'cors',
headers: headers,
credentials: 'include'
})
.then(function(response){
return response.text();
/* if (response.status === 200 || response.status === 0) {
// Success!
console.log('Success: ' + response.text());
return {
success: true,
text: response.text()
};
} else {
// Failure!
console.log('Fail: ' + response.statusText);
return {
success: false,
text: response.statusText
};
} */
} )
.then(function(rspText){
// The raw response contains decoded HTML tags... we need to clean that up.
// Remove dashes from the xml responses... the eventual js object wont like them
rspText = rspText.replace(/-/g, "");
// Convert the text response to XML
var parser = new DOMParser;
var dom = parser.parseFromString(
rspText,
'text/html');
var decodedString = dom.body.textContent;
// use the DOMParser browser API to convert text to a Document
var XML = new DOMParser().parseFromString(decodedString, "text/xml");
// and then use #parse to convert it to a JS object
var responseXmlObject = parse(XML);
console.log(responseXmlObject);
success = true;
text = responseXmlObject.messages;
alert(responseXmlObject.messages.messagedetail);
})
.catch(function(error) {
// Networking Failure!
console.log('NetworkFail: ' + error);
success = false;
text = error;
})
.then(function () {
console.log({
success: success,
text: text
});
return {
success: success,
text: text
};
})
//.done();
}
};
You should be returning the promise from fetch inside the submit function so that the function in App.js can wait until fetch is done to do processing

Related

multer: Uploading Excel file to database using node.js returning undefined

I am having troubles trying to update an excel file to a database`s table. The data is being inserted into the database's table but i still getting an error Alert Fail! (object object) on the static javascript file. Maybe should I use fetch instead of ajax?
Even though I am inserting data into the database through the function, these are the errors displayed on the brownser.
'http://127.0.0.1:8006/api/file/upload' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
jquery.min.js:2 POST http://127.0.0.1:8006/api/file/upload net::ERR_FAILED
the bit of my front end code is that:
javascript:
$(document).ready(function() {
/**
* Upload single file to SpringBoot
* at RestAPI: /api/upload/file/single
*/
$("#uploadSingleFileForm").submit(function(evt) {
evt.preventDefault();
let formData = new FormData($(this)[0]);
$.ajax({
url : apiServer + 'file/upload',
type : 'POST',
data : formData,
async : false,
cache : false,
contentType : false,
enctype : 'multipart/form-data',
processData : false,
success : function(response) {
$("#response").empty();
if(response.status !== "error"){
let displayInfo = response.filename + " : " + response.message + "<br>";
$("#response").append(displayInfo);
// add some css
$("#response").css("display", "block");
$("#response").css("background-color", "#e6e6ff");
$("#response").css("border", "solid 1px black");
$("#response").css("border-radius", "3px");
$("#response").css("margin", "10px");
$("#response").css("padding", "10px");
}else{
$("#response").css("display", "none");
let error = response.error;
alert(error);
}
},
error: function(e){
alert("Fail! " + e);
}
});
return false;
});
/**
* Upload Multiple Files to SpringBoot RestAPI
*/
$("#uploadMultipleFilesForm").submit(function(evt) {
evt.preventDefault();
let formData = new FormData($(this)[0]);
$.ajax({
url : apiServer + 'file/multiple/upload',
type : 'POST',
data : formData,
async : false,
cache : false,
contentType : false,
enctype : 'multipart/form-data',
processData : false,
success : function(response) {
$("#responses").empty();
let displayInfo = "<ul>";
for(let i=0; i<response.length; i++){
displayInfo += "<li>" + response[i].filename + " : " + response[i].message;
displayInfo += "</li>";
}
$("#responses").append(displayInfo + "</ul>");
$("#responses").css("display", "block");
// add some css
$("#responses").css("background-color", "#e6e6ff");
$("#responses").css("border", "solid 1px black");
$("#responses").css("border-radius", "3px");
$("#responses").css("margin", "10px");
$("#responses").css("padding", "10px");
},
error: function(e){
alert("Fail! " + e);
}
});
return false;
});
})
that is the excel.controller.js - back-end
var stream = require('stream');
var await = require('await')
const db = require('./db.config.js');
const stckinvt = db.stckinvt;
const excel = require('exceljs');
const readXlsxFile = require('read-excel-file/node');
exports.uploadFile = (req, res) => {
try{
let filePath = __basedir + "/uploads/" + req.file.filename;
readXlsxFile(filePath).then(rows => {
// `rows` is an array of rows
// each row being an array of cells.
console.log(rows);
// Remove Header ROW
rows.shift();
const stockinventory = [];
let length = rows.length;
for(let i=0; i<length; i++){
let stckinvt = {
id: rows[i][0],
store: rows[i][1],
itemNo: rows[i][2],
itemName: rows[i][3],
}
stockinventory.push(stckinvt);
}
stckinvt.bulkCreate(stockinventory).then(() => {
const result = {
status: "ok",
filename: req.file.originalname,
message: "Upload Successfully!",
}
res.json(result);
});
});
}catch(error){
const result = {
status: "fail",
filename: req.file.originalname,
message: "Upload Error! message = " + error.message
}
res.json(result);
}
}
EDIT: I found out what was wrong, and it is related to the Restify Plugin API bodyParser:
If I change this line:
server.use([cors.actual, restify.plugins.queryParser(), restify.plugins.bodyParser()]);
To this:
server.use([cors.actual, restify.plugins.queryParser()]);
It works perfectly but I am afraid I can not just delete this information because can be important to other pages on the server. Any ideas?

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 can I check a fetch response to call another one?

I trying to get a download of a json file from an API.
To do that, I need to call 3 endpoints.
http://url.com/export
it returns a json: {"exportLoading":true,"file":"export-20190618-183316.json"}
After that I should call the second endpoint and check the status of this exportation:
http://url.com/export/status
it returns true or false (while server is processing, this endpoint returns true. When it returns false the file is done to make a download.)
So, if the status === false, I can call the last endpoint
http://url.com/download/file_name (I make this request passing the file name - returned from the first request - to download the file.
My question is, how can I check if the second endpoint returns false to make the last request and download the file?
I just did it until the second endpoint.
app.get('/export', function (req, res, next) {
global.fetch = fetch
global.Headers = fetch.Headers;
const headers = new Headers();
const username = 'user';
const password = 'pass';
const URL = 'http://url.com/export'
headers.set('Authorization', 'Basic ' + base64.encode(username + ":" + password));
fetch(URL, {
method: 'GET',
headers: headers,
})
.then(res => res.json())
.then(json => {
fetch("http://url.com/exportl/status", {
method: 'GET',
headers: headers,
}).then(result => ...)
})
.catch(function (error) {
console.log(error)
})
});
You could use a while loop that will call the endpoint until the condition is met:
app.get('/export', async function(req, res, next) {
global.fetch = fetch
global.Headers = fetch.Headers;
const headers = new Headers();
const username = 'user';
const password = 'pass';
const URL = 'http://url.com/export'
headers.set('Authorization', 'Basic ' + base64.encode(username + ":" + password));
fetch(URL, {
method: 'GET',
headers: headers,
}).then(r => r.json)
.then(data => {
// use data here
var status = false
while (!status) {
status = await checkStatus()
}
// third call
})
});
function checkStatus() {
return fetch("http://url.com/exportl/status", {
method: 'GET',
headers: headers,
}).then(r => r.json)
}
Note, I do not know the response from the status, you will have to change the code to accommodate the response.

How to dynamically update a value inside the string for req.write('{ "upc":"make-this-dynamic"}')?

MY problem is using the API that uses req.write('{ "upc":"604214384507"}') to look up an item- I cannot get it to populate with dynamic values outside of the string, it seems to be messing with the syntax that the API requires. Is there a syntax I could use to preserve API needed format but use a dynamic variable to fill out the upc number?
let UPC = 604214384507
req.write(`'{ "upc":"${UPC}"}'`)
results in undefined
const https = require('https')
var opts = {
hostname: 'api.upcitemdb.com',
path: '/prod/trial/lookup',
method: 'POST',
headers: {
"Content-Type": "application/json",
}
}
var req = https.request(opts, function (res) {
console.log('statusCode: ', res.statusCode);
console.log('headers: ', res.headers);
res.on('data', function (UPCoutput) {
var UPCparsed = JSON.parse(UPCoutput);
console.log('Product: ' + UPCparsed.items[0].title);
console.log('UPC: ' + UPCparsed.items[0].upc);
console.log('Image: ' + UPCparsed.items[0].images[0]);
console.log('Price: ' + UPCparsed.items[0].offers[0].price);
})
})
req.on('error', function (e) {
console.log('problem with request: ' + e.message);
});
req.write('{ "upc":"604214384507"}')
req.end()
I expect to be able to dynamically feed the UPC number to the API to pull data.
So just write that like you did earlier:
req.write(`{ "upc": "${UPC}" }`);
I needed to use JSON.strigify
const userUPC = "604214384507"
const searchAPI = { upc: userUPC }
JSON.stringify(UPC)
req.write(JSON.stringify(searchAPI))
req.end()

ReferenceError: $http is not defined

I am trying to make a SOAP call through node.js and am getting the below error:
ReferenceError: $http is not defined
Here is my code, everything else appears to work until it fails at the last line:
//Import the `assert` module to validate results.
var assert = require('assert');
var SoapRequestXML='<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:_5="https://clients.mindbodyonline.com/api/0_5">\r\n' +
'<soapenv:Header/>\r\n' +
'<soapenv:Body>\r\n' +
'<_5:GetClasses>\r\n' +
'<_5:Request>\r\n' +
'<_5:SourceCredentials>\r\n' +
'<_5:SourceName>SOURCECREDENTIALS</_5:SourceName>\r\n' +
'<_5:Password>PASSWORD</_5:Password>\r\n' +
'<_5:SiteIDs>\r\n' +
'<_5:int>-99</_5:int>\r\n' +
'</_5:SiteIDs>\r\n' +
'</_5:SourceCredentials>\r\n' +
'</_5:Request>\r\n' +
'</_5:GetClasses>\r\n' +
'</soapenv:Body>\r\n' +
'</soap:Envelope>';
var options = {
//Define endpoint URL.
url: "https://api.mindbodyonline.com/0_5/ClassService.asmx",
//Define body of POST request.
body: SoapRequestXML,
//Define insert key and expected data type.
headers: {
'POST': 'https://api.mindbodyonline.com/0_5/ClassService.asmx?wsdl HTTP/1.1',
'Accept-Encoding': 'gzip,deflate',
'Content-Type': 'text/xml;charset=UTF-8',
'SOAPAction': '"http://clients.mindbodyonline.com/api/0_5/GetClasses"',
'Content-Length': '594',
'Host': 'api.mindbodyonline.com',
'Connection': 'Keep-Alive',
'User-Agent': 'Apache-HttpClient/4.1.1 (java 1.5)'
}
};
//Define expected results using callback function.
function callback(error, response, body) {
//Log status code to Synthetics console.
console.log(response);
//Verify endpoint returns 200 (OK) response code.
assert.ok(response.statusCode == 200, 'Expected 200 OK response');
//Parse JSON received from Insights into variable.
//
var parseString = require('xml2js').parseString;
var XMLReSULT = response.body;
parseString(XMLReSULT, function (err, result) {
console.dir(result);
});
//Log end of script.
console.log("End reached");
}
//Make GET request, passing in options and callback.
$http.post(options, callback);
Any help is appreciated, I am very new at this.
Like previous comments implied, $http is not defined, and you cant use var $http = require('http') because nodes http does not have a post method. You will need to do a little refactoring. I think you're looking for something like this.
var assert = require('assert')
var http = require('http')
var parseString = require('xml2js').parseString;
var SoapRequestXML='<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:_5="https://clients.mindbodyonline.com/api/0_5">\r\n' +
'<soapenv:Header/>\r\n' +
'<soapenv:Body>\r\n' +
'<_5:GetClasses>\r\n' +
'<_5:Request>\r\n' +
'<_5:SourceCredentials>\r\n' +
'<_5:SourceName>SOURCECREDENTIALS</_5:SourceName>\r\n' +
'<_5:Password>PASSWORD</_5:Password>\r\n' +
'<_5:SiteIDs>\r\n' +
'<_5:int>-99</_5:int>\r\n' +
'</_5:SiteIDs>\r\n' +
'</_5:SourceCredentials>\r\n' +
'</_5:Request>\r\n' +
'</_5:GetClasses>\r\n' +
'</soapenv:Body>\r\n' +
'</soap:Envelope>';
var options = {
hostname: 'api.mindbodyonline.com',
port: 80,
path: '/0_5/ClassService.asmx',
method: 'POST',
headers: {
'Accept-Encoding': 'gzip,deflate',
'Content-Type': 'text/xml;charset=UTF-8',
'SOAPAction': '"http://clients.mindbodyonline.com/api/0_5/GetClasses"',
'Content-Length': '594',
'Connection': 'Keep-Alive',
'User-Agent': 'Apache-HttpClient/4.1.1 (java 1.5)'
}
}
var req = http.request(options, (res) => {
var data;
res.setEncoding('utf8');
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
parseString(data, function(err, result) {
console.log(result);
});
console.log('End reached')
});
});
req.on('error', (e) => {
console.log(`problem with request: ${e.message}`);
});
req.write(SoapRequestXML);
req.end();

Categories