Not resolved promises with File objects after POST form requests - ajax

I'm using fetch Web API to retrieve file content from a couple of URLs and send it to a form. However, when sent to the backend from a form POST ajax request, instead of having the actual File Objects there, I'm retrieving Promise objects (which cannot be handled at the backend).
What is wrong in the code below that prevents infile and mapfile variables when sent to $.ajax to have a File object?
let formData = new FormData();
let exampleb2pPromise = fetch( fileHash["infile"].fileURL).then(function(response) {
fileHash["infile"].blob = response.blob();
return fileHash;
}).then(function(fileHash) {
let infile = new File( [ fileHash["infile"].blob ], fileHash["infile"].filename, { type: fileHash["infile"].filetype } );
return infile;
});
let exampleCSVPromise = fetch(fileHash["mapfile"].fileURL).then(function(response) { ...
Promise.all([exampleb2pPromise, exampleCSVPromise]).then(function(values) {
let infile = values[0];
let mapfile = values[1];
if ( infile && mapfile ) ) {
console.log( infile );
formData.append( "infile", infile, "input.tsv" );
if ( mapfile ) {
console.log( mapfile );
formData.append( "mapfile", mapfile, "mapfile.txt" );
}
}
return formData;
}).then( function( formData ) {
let exec = "/submit";
console.log( formData );
$.ajax({
url : exec,
type: "POST",
data : formData,
processData: false,
contentType: false,
success:function(data, textStatus, jqXHR){
if ( data && data.session ) {
...
}
},
error: function(jqXHR, textStatus, errorThrown){
//if fails
// TODO: To be handled
}
});
});

response.blob() returns a Promise - you'd need to rewrite your code something like
let exampleb2pPromise = fetch( fileHash["infile"].fileURL).then(function(response) {
return response.blob();
}).then(function(blob) {
fileHash["infile"].blob = blob;
return fileHash;
}).then(function(fileHash) {
let infile = new File( [ fileHash["infile"].blob ], fileHash["infile"].filename, { type: fileHash["infile"].filetype } );
return infile;
});
Although, it's far easier to write it like:
let exampleb2pPromise = fetch( fileHash.infile.fileURL)
.then(response => response.blob())
.then(blob => new File(fileHash.infile.blob = blob, fileHash.infile.filename, { type: fileHash.infile.filetype } ));

Related

Tabulation added to returned ajax call data

my problem is that whenever I do an ajax call and console.log the returned data, there is a tabulation in front of my returned data. Is this normal? If so, is there a way to strip the tabulation from the data variable? My code is below.
function search_rhymes_get_rhyme_content() {
echo 'Test';
die();
}
$.ajax( {
url: rhymes_ajax_obj.ajaxurl,
data: {
'action': 'search_rhymes_get_rhyme_content'
},
success: function( data ) {
console.log( 'Test:' + data );
},
error: function( error ) {
console.log( error );
}
} );
Thanks
I got it working. Output: https://ibb.co/QQHGgXV
I added the following line before my console.log:
data = data.trim();
Here's the updated code with the solution:
function search_rhymes_get_rhyme_content() {
echo 'Test';
die();
}
$.ajax( {
url: rhymes_ajax_obj.ajaxurl,
data: {
'action': 'search_rhymes_get_rhyme_content'
},
success: function( data ) {
console.log( 'Test1:' + data );
data = data.trim();
console.log( 'Test2:' + data );
},
error: function( error ) {
console.log( error );
}
} );

Cypress - unable to store response.body data into a JSON file

I've created a POST XMLHttpRequest with FormData successfully. I now need to capture it's response body and get it stored in a JSON file.
Cypress.Commands.add(
"Post_Clients",
(imagePath, imageType, attr1, attr2, attr1Val, done) => {
cy.fixture(imagePath, "binary").then(imageBin => {
Cypress.Blob.binaryStringToBlob(imageBin, imageType).then(blob => {
const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
const data = new FormData();
data.set(attr1, attr1Val);
data.set(attr2, blob);
xhr.open("POST", "https://api.teamapp.myhelpling.com/admin/clients");
xhr.responseType = "json"
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("access-token", accesstoken);
xhr.setRequestHeader("client", client);
xhr.setRequestHeader("expiry", expiry);
xhr.setRequestHeader("token-type", tokentype);
xhr.setRequestHeader("uid", uid);
xhr.onload = function() {
done(xhr);
};
xhr.onerror = function() {
done(xhr);
};
xhr.send(data);
});
});
}
);
it.only("API POSTing TEST", () => {
cy.Post_Clients(
"/images/clients/Golden JPEG.jpeg",
"image/jpeg",
"client[name]",
"client[client_logo_attributes][content]",
"Test Attr 1 Value is Hi!!!",
resp => {
cy.writeFile(
"cypress/fixtures/POST API OUTPUT DATA/Client.json",
resp.response
);
expect(response.status).to.eq(201);
}
);
});
Kindly note that expect(response.status).to.eq(201); assertion works well.
Following code logs the body properly in the console
cy.log("Response Body", resp.response);
console.log("Response Body", resp.response);
Response Body is: -
{"client":{"id":452,"name":"Test Attr 1 Value is Hi!!!","client_logo":{"id":543,"path":"https://api.teamapp.myhelpling.com/uploads/client_images/6279486665-1551780183.","thumb":"https://api.teamapp.myhelpling.com/uploads/client_images/thumb_6279486665-1551780183.","medium":"https://api.teamapp.myhelpling.com/uploads/client_images/medium_6279486665-1551780183.","large":"https://api.teamapp.myhelpling.com/uploads/client_images/medium_6279486665-1551780183.","filename":"blob","ratio":1.78}}}
but
cy.writeFile(
"cypress/fixtures/POST API OUTPUT DATA/Client.json",resp.response
);
doesn't save the response body in Client.JSON file.
cy.writeFile seems to not work in this code. I've verified this by
passing a JSON e.g. {"A":"B"} and that too didn't make it to the
JSON.
Thanks everyone for all you kind help. I've made it work by calling cy.writeFile inside onLoad event before triggering XHR request. Here's the code sample with some other updates that I've made for my other works: -
Cypress.Commands.add(
"Post_Bucket",
(imagePath, imageType, title, img, titleVal) => {
cy.fixture(imagePath, "binary").then(imageBin => {
Cypress.Blob.binaryStringToBlob(imageBin, imageType).then(blob => {
const xhr = new XMLHttpRequest();
const data = new FormData();
data.set(title, titleVal);
data.set(img, blob);
cy.readFile(Cypress.env("IDStore")).then(obj => {
xhr.open(
"POST",
Cypress.env("BucketPostURLPart1") +
obj.journeyID +
Cypress.env("BucketPostURLPart2"),
false
);
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("access-token", accesstoken);
xhr.setRequestHeader("client", client);
xhr.setRequestHeader("expiry", expiry);
xhr.setRequestHeader("token-type", tokentype);
xhr.setRequestHeader("uid", uid);
xhr.onload = function() {
if (this.status === 201) {
cy.writeFile(
Cypress.env("BucketOutputFile"),
JSON.parse(this.responseText)
);
cy.readFile(Cypress.env("IDStore")).then(obj => {
obj.bucketID = JSON.parse(this.responseText).bucket.id;
cy.writeFile(Cypress.env("IDStore"), obj);
});
}
};
xhr.send(data);
});
});
});
}
);
This is the simple example try with this one.
cy.request('https://jsonplaceholder.cypress.io/users')
.then((response) => {
cy.writeFile('cypress/fixtures/users.json', response.body)
})

uploading profile pic in hapijs 17.0

I am using hapijs version 17.0.1. I am trying to upload an image using ajax request on a hapijs route. Here is my AJAX code to upload profile pic:
var image_file_input = document.getElementById("user_profile_upload");
image_file_input.onchange = function () {
if(this.files != undefined)
{
if(this.files[0] != undefined)
{
var formData = tests.formdata ? new FormData() : null;
if (tests.formdata)
{
//alert(file)
formData.append('image_file', this.files[0]);
formData.append('userId', user_id);
formData.append('memberId', member_id);
}
$.ajax({
url: "/v1/User/uploadUserPic",
data: formData,
type: "POST",
dataType: "json",
contentType: false,
processData: false,
contentType: "multipart/form-data",
success: function(data){
console.log(data);
var errMsg = null;
var resData = null;
if(data.statusCode == 200)
{
resData = data.result;
}
else
{
alert(data.message)
}
},
error: function(error){
alert(error);
}
});
}
}
}
And here is my Hapijs route Code:
var uploadUserPic = {
method: 'POST',
path: '/v1/Module/uploadUserPic',
config: {
description: 'Update Image For User',
tags: ['api', 'User'],
auth: 'session',
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
userId : Joi.string().regex(/^[a-f\d]{24}$/i).required(),
memberId: Joi.string().required(),
image_file: Joi.object().required(),
},
failAction: FailCallBack
}
},
handler: function (request, reply) {
var resultData = null;
var error = null;
return new Promise(function (resolve) {
var multiparty = require('multiparty');
var fs = require('fs');
var form = new multiparty.Form();
form.parse(request.payload, function (err, fields, files) {
if(err)
{
error = err;
resolve();
}
else
{
var mkdirp = require('mkdirp');
var img_dir = "./files/users/";
mkdirp(img_dir, function (err) {
if (err)
{
error = err;
console.error(err);
resolve();
}
else
{
var oldpath = files.image_file.path;
var newpath = "./files/users/"+requestPayload.userId+".png";
fs.rename(oldpath, newpath, function (err) {
if(err)
{
error = err;
}
resolve();
});
}
});
}
});
}).then(function (err, result) {
if(err) return sendError(err);
if(error) return sendError(error)
return {
"statusCode": 200,
"success": true
};
});
}
}
The above code gives me following error cannot read property 'content-length' of undefined on line form.parse(request.payload, function (err, fields, files) {});
Please let me know If I am doing something wrong. If I replace the url in ajax request with anohter url that I have written in php then it works perfectly. which means that something is wrong with my hapijs/nodejs code.
There's a good post on how to handle file uploads in Hapi.js (written in version 16) https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js
Since you are using payload.parse = true, I am not seeing a particular reason why you have to use multiparty. I have the following working code that would save files (of any type) uploaded from client into uploads directory on the server (Please do not use directly on production as no sanitation is done)
{
path: '/upload',
method: 'POST',
config: {
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
files: Joi.array().single()
}
}
},
handler: function(request) {
const p = request.payload, files = p.files
if(files) {
console.log(`${files.length} files`)
files.forEach(async file => {
const filename= file.hapi.filename
console.log(`Saving ${filename} to ./uploads`)
const out = fs.createWriteStream(`./uploads/${filename}`)
await file.pipe(out)
})
}
return {result: 'ok'}
}
}
You can use the following curl command to test
curl http://localhost:8080/upload -F 'files=#/path/to/a/note.txt' -F 'files=#/path/to/test.png' -vvv
There are a few issues with your code. First in your $.ajax call, you have specified contentType twice, although it's not a syntax error but it's careless to code like that. Second the function's signature inside your .then() block is incorrect. You are mixing the idea of Promise and callback. I don't think the following line will be triggered
if(err) return sendError(err);
One last trivial thing, you said you are using Hapi 17 but based on the handler function's signature
handler: function (request, reply) {
...
Seems you are not totally onboard with Hapi17 as the new signature is
handler: function (request, h) {
And it's not just the rename of reply to h.

How can I handle a ajax request response in the Flux Architecture?

Looking at the Flux Documentation I can't figure out how the code to a ajax update, and a ajax fetch would fit into the dispatcher, store, component architecture.
Can anyone provide a simple, dummy example, of how an entity of data would be fetched from the server AFTER page load, and how this entity would be pushed to the server at a later date. How would the "complete" or "error" status of request be translated and treated by the views/components? How would a store wait for the ajax request to wait? :-?
Is this what you are looking for?
http://facebook.github.io/react/tips/initial-ajax.html
you can also implement a fetch in the store in order to manage the information.
Here is an example (it is a concept, not actually working code):
'use strict';
var React = require('react');
var Constants = require('constants');
var merge = require('react/lib/merge'); //This must be replaced for assign
var EventEmitter = require('events').EventEmitter;
var Dispatcher = require('dispatcher');
var CHANGE_EVENT = "change";
var data = {};
var message = "";
function _fetch () {
message = "Fetching data";
$.ajax({
type: 'GET',
url: 'Url',
contentType: 'application/json',
success: function(data){
message = "";
MyStore.emitChange();
},
error: function(error){
message = error;
MyStore.emitChange();
}
});
};
function _post (myData) {
//Make post
$.ajax({
type: 'POST',
url: 'Url',
// post payload:
data: JSON.stringify(myData),
contentType: 'application/json',
success: function(data){
message = "";
MyStore.emitChange();
},
error: function(error){
message = "update failed";
MyStore.emitChange();
}
});
};
var MyStore = merge(EventEmitter.prototype, {
emitChange: function () {
this.emit(CHANGE_EVENT);
},
addChangeListener: function (callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function (callback) {
this.removeListener(CHANGE_EVENT, callback);
},
getData: function (){
if(!data){
_fetch();
}
return data;
},
getMessage: function (){
return message;
},
dispatcherIndex: Dispatcher.register( function(payload) {
var action = payload.action; // this is our action from handleViewAction
switch(action.actionType){
case Constants.UPDATE:
message = "updating...";
_post(payload.action.data);
break;
}
MyStore.emitChange();
return true;
})
});
module.exports = MyStore;
Then you need to subscribe your component to the store change events
var React = require('react');
var MyStore = require('my-store');
function getComments (){
return {
message: null,
data: MyStore.getData()
}
};
var AlbumComments = module.exports = React.createClass({
getInitialState: function() {
return getData();
},
componentWillMount: function(){
MyStore.addChangeListener(this._onChange);
},
componentWillUnmount: function(){
MyStore.removeChangeListener(this._onChange);
},
_onChange: function(){
var msg = MyStore.getMessage();
if (!message){
this.setState(getData());
} else {
this.setState({
message: msg,
data: null
});
}
},
render: function() {
console.log('render');
return (
<div>
{ this.state.message }
{this.state.data.map(function(item){
return <div>{ item }</div>
})}
</div>
);
}
});
I hope it is clear enough.

Jquery mobile autocomplete implementation with local json

I am trying to implement the jquery mobile autocomplete functionality, because i have a list of around 3000 items.
I have a json that has this structure:
[{"v1":" abcd","v2":"http://www.url.nl","v3":0487,"v4":"street12","v5":"H"},
{"v1":" qq","v2":"http://www.url.nl","v3":0297,"v4":"street14","v5":"A"},
{"v1":" zz","v2":"http://www.url.nl","v3":0117,"v4":"street55","v5":"A"}]
I am using this example: http://jquerymobile.com/demos/1.3.0-rc.1/docs/demos/widgets/autocomplete/autocomplete-remote.html
but i can not get this to work with my json file.
$( document ).on( "pageinit", "#myPage", function() {
$( "#autocomplete" ).on( "listviewbeforefilter", function ( e, data ) {
var $ul = $( this ),
$input = $( data.input ),
value = $input.val(),
html = "";
$ul.html( "" );
if ( value && value.length > 2 ) {
$ul.html( "<li><div class='ui-loader'><span class='ui-icon ui-icon-loading'></span></div></li>" );
$ul.listview( "refresh" );
$.ajax({
url: "http://gd.geobytes.com/AutoCompleteCity",
dataType: "jsonp",
crossDomain: true,
data: {
q: $input.val()
}
})
.then( function ( response ) {
$.each( response, function ( i, val ) {
html += "<li>" + val + "</li>";
});
$ul.html( html );
$ul.listview( "refresh" );
$ul.trigger( "updatelayout");
});
}
});
});
I have the feeling that it has to with the ajax call and my json file, but I haven't found the solution yet.

Resources