Don't include in promise when archive is undefined - javascript

I have this code to verify if archives are undefined, but in this way I just verify if ALL the archives are undefined, I want to verify if each one is undefined.
var render_archive1 = jQuery('#archive-pratica1')[0].files[0];
var render_archive2 = jQuery('#archive-pratica2')[0].files[0];
var render_archive3 = jQuery('#archive-pratica3')[0].files[0];
var render_archive4 = jQuery('#archive-pratica4')[0].files[0];
if (render_archive1 !== undefined && render_archive2 !== undefined && render_archive3 !== undefined && render_archive4 !== undefined) {
// wait till file gets encoded
Promise.all([getBase64(render_archive1), getBase64(render_archive2), getBase64(render_archive3), getBase64(render_archive4)]).then(([data, data1, data2, data3]) => {
// push file data
form.push({'name': 'archive_pratica1', 'value': data});
form.push({'name': 'archive_pratica2', 'value': data1});
form.push({'name': 'archive_pratica3', 'value': data2});
form.push({'name': 'archive_pratica4', 'value': data3});
// send request
jQuery.ajax ({
type: "POST",
data: {
action: 'sendEditInfo',
form: form
},
url: '../wp-admin/admin-ajax.php'
})
.then((res) => {
}, err => {
});
})
}
When an archive is undefined, I can't send an ajax request because ir returns an error in the getBase64 function. So, I need to create an if to verify when each one is undefined.
function getBase64
function getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}

You could do this refactor in order to archive this task:
function readArchive(archiveId) {
var renderArchive = jQuery(`#${archiveId}`)[0].files[0];
if (renderArchive) {
return getBase64(renderArchive)
.then(data => ({ name: archiveId, data }));
}
return Promise.resolve(null);
}
Promise.all([
readArchive('archive-pratica1'),
readArchive('archive-pratica2'),
readArchive('archive-pratica3'),
readArchive('archive-pratica4'),
])
.then((archives) => {
archives
.filter(_ => _ !== null)
.forEach(_ => form.push({ 'name': _.name, 'value': _.data }));
// send request
jQuery.ajax({
type: "POST",
data: {
action: 'sendEditInfo',
form: form
},
url: '../wp-admin/admin-ajax.php'
})
})
.catch((err) => {
// do something
});

Don't reject your promises if they are undefined
function getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => resolve({error:error});
});
}
then you test if the result has a error
if(!data.error)
form.push({'name': 'archive_pratica1', 'value': data});

If you want to make requests only for those not undefined try something like this:
var promises = [];
for(var i = 1; i <= 4: i++) {
renderArchive = jQuery('#archive-pratica' + i)[0].files[0];
if (renderArchive !== undefined) {
var p = getBase64(renderArchive).then((data) => {
form.push({'name': 'archive_pratica' + i, 'value': data});
});
promises.push(p);
}
}
Promise.all(promises).then(() => {
jQuery.ajax ({
type: "POST",
data: {
action: 'sendEditInfo',
form: form
},
url: '../wp-admin/admin-ajax.php'
});
});

Related

How to save pdf to Cloudant

I want to save the pdf to Cloudant. With the code below, I get an error opening the Attachment in Cloudant. "An error was encountered when processing this file"
I can put fake string data in the "._attachments[name].data" field and it will save.
The Cloudant docs say the data content needs to be in base64 and that is what I am attempting.
Cloudant says "The content must be provided by using BASE64 representation"
function saveFile() {
var doc = {};
var blob = null;
//fileName is from the input field model data
var url = fileName;
fetch(url)
.then((r) => r.blob())
.then((b) => {
blob = b;
return getBase64(blob);
})
.then((blob) => {
console.log(blob);
let name = url._rawValue.name;
doc._id = "testing::" + new Date().getTime();
doc.type = "testing attachment";
doc._attachments = {};
doc._attachments[name] = {};
doc._attachments[name].content_type = "application/pdf";
doc._attachments[name].data = blob.split(",")[1];
console.log("doc: ", doc);
})
.then(() => {
api({
method: "POST",
url: "/webdata",
auth: {
username: process.env.CLOUDANT_USERNAME,
password: process.env.CLOUDANT_PASSWORD,
},
data: doc,
})
.then((response) => {
console.log("result: ", response);
alert("Test has been submitted!");
})
.catch((e) => {
console.log("e: ", e);
alert(e);
});
console.log("finished send test");
});
}
function getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
}
any ideas?
Thanks
CouchDB, and by extension Cloudant, has a means of handling a "multi-part" request where the JSON document and the attachments are sent in the same request. See https://docs.couchdb.org/en/3.2.2/api/document/common.html#put--db-docid
They are modelled in CouchDB's Nano project here: https://www.npmjs.com/package/nano#multipart-functions
const fs = require('fs');
fs.readFile('rabbit.png', (err, data) => {
if (!err) {
await alice.multipart.insert({ foo: 'bar' }, [{name: 'rabbit.png', data: data, content_type: 'image/png'}], 'mydoc')
}
});
Alternatively, you could write the document first and add the attachment in a supplementary request. Using the current Cloudant SDKs:
write document https://cloud.ibm.com/apidocs/cloudant?code=node#putdocument
write attachment https://cloud.ibm.com/apidocs/cloudant?code=node#putattachment
const doc = {
a: 1,
b: 2
}
const res = await service.putDocument({
db: 'events',
docId: 'mydocid',
document: doc
})
const stream = fs.createReadStream('./mypdf.pdf')
await service.putAttachment({
db: 'events',
docId: 'mydocid',
rev: res.result.rev, // we need the _rev of the doc we've just created
attachmentName: 'mypdf',
attachment: stream,
contentType: 'application/pdf'
})
I found out I was doing too much to the PDF file. No need to make to blob then convert to base64.
Only convert to base64.
async function sendFiles() {
try {
const url = fileName;
const doc = {};
doc._attachments = {};
doc._id = "testing::" + new Date().getTime();
doc.type = "testing attachment";
for (let item of url._value) {
const blob2 = await getBase64(item);
let name = item.name;
doc._attachments[name] = {};
doc._attachments[name].content_type = item.type;
doc._attachments[name].data = blob2.split(",")[1];
}
const response = await api({
method: "POST",
url: "/webdata",
data: doc,
});
} catch (e) {
console.log(e);
throw e; // throw error so caller can see the error
}
console.log("finished send test");
fileName.value = null;
}
function getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
}
This works for me.

Returning promises up a function chain

Below is the structure of some code that I have written. I am attempting to load a number of files to a server using ajax and once all are complete, do some action.
function func1(items){
const results = []
for (let i=0; i<items.length; i++) {
results[i] = func2();
}
Promise.all(results).then(response => some_action())
}
function func3(params) {
return new Promise((resolve, reject) => {
//ajax call here and resolve/reject
})
}
function func2(){
if(stuff){
return func3(some_params);
} else {
return func3(other_params);
}
}
Unfortunately, it is not working as I expected. The array results is not an array of promises, but an array of undefined. I am new to Javascript promises, so any help would be appreciated.
EDIT: to respond to the comment about the possibility of a silent return, I post the actual code for func2 (lightly modified):
function func2(item, id, id2, top_level, path){
if(item.isFile){
item.file(function(file) {
if(file.name.substring(file.name.lastIndexOf('.')+1) === "docx"){
file = new File([file], file.name, {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
})
}
if(file.name !== "desktop.ini"){
let url = 'url'
return func3(file, id, url, "", false);
}
});
} else {
window.parent.ipcRenderer.send('zip-folder', path);
window.parent.ipcRenderer.on('zipped', (event, buffer) => {
var zipped_file = new File([buffer], item.name + ".zip", {
type: "application/zip"
})
let url = "/url"
return func3(zipped_file, id, url, id2, true);
})
}
}
Your else block does not return anything:
...
else {
window.parent.ipcRenderer.send('zip-folder', path);
window.parent.ipcRenderer.on('zipped', (event, buffer) => {
var zipped_file = new File([buffer], item.name + ".zip", {
type: "application/zip"
})
let url = "/url"
return func3(zipped_file, id, url, id2, true);
})
// silently returns undefined
}
Don't be fooled by the return statement in the anonymous function. It returns the anonymous function, not func2:
window.parent.ipcRenderer.on('zipped', return this
(event, buffer) => { <----------.
var zipped_file = new File([buffer], item.name + ".zip", { |
type: "application/zip" |
}) |
let url = "/url" |
return func3(zipped_file, id, url, id2, true); -------------------'
}
)
This would be more obvious if you rewrite your code without using anonymous functions:
function func4(id, url, id2) {
return function (event, buffer) {
var zipped_file = new File([buffer], item.name + ".zip", {
type: "application/zip"
})
let url = "/url"
return func3(zipped_file, id, url, id2, true);
}
}
function func2(item, id, id2, top_level, path){
if(item.isFile){
item.file(function(file) {
if(file.name.substring(file.name.lastIndexOf('.')+1) === "docx"){
file = new File([file], file.name, {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
})
}
if(file.name !== "desktop.ini"){
let url = 'url'
return func3(file, id, url, "", false);
}
});
} else {
window.parent.ipcRenderer.send('zip-folder', path);
window.parent.ipcRenderer.on('zipped', func4(id, url, id2))
// no return statement !!
}
}
One way to get around it is to convert it into a promise:
return new Promise((ok,fail) => {
window.parent.ipcRenderer.on('zipped', (event, buffer) => {
var zipped_file = new File([buffer], item.name + ".zip", {
type: "application/zip"
})
let url = "/url"
ok(func3(zipped_file, id, url, id2, true));
})
});
Of course, depending on how your error logic flows you may want to wrap the promise at a higher level. This merely illustrates a quick fix.
You need to return new Promise in EVERY function !

My promise is being resolved before my second query is able to run inside of a .map function

const getAdditionalInfosPromise = (ProgramID, LeadId) => {
return new Promise((resolve, reject) => {
connection.query(`SELECT * FROM crm_additionalLeadInfoFields WHERE ProgramID= ${ProgramID};`, (error, response) => {
if (error) reject(console.error(error, 'SQL ERROR IN GETADDITIONALINFOPROMISE'));
else {
let getAdditionalInfosData = [];
const getAdditionalInfos = response;
getAdditionalInfos.map((data, i) => {
​
let tableNameData;
// checking to see what our table name is
if (data.TableName === 'leads') {
tableNameData = `WHERE id= ${LeadId};`
}
else {
tableNameData = `Where LeadId = ${LeadId};`
}
​
// Remove any white spaces
​
const columnName = data.ColumnName.replace(/ +/g, "")
​
connection.query(`SELECT ${columnName} as pertinentValue
FROM ${data.TableName}
${tableNameData}`, (err, res) => {
if (err) console.error(err, 'MY SQL ERR IN GETADDITIONALINFOSPROMISE');
else {
console.log(data);
if (data.DisplayName !== 'Dealer Name') {
const pertinentValue = res[0].pertinentValue
​
getAdditionalInfosData.push({
'id': `additionalItem${i}`,
'label': data.DisplayName,
'value': `${pertinentValue !== null ? pertinentValue : ''}`,
'class': ''
})
​
}
}
})
})
resolve(getAdditionalInfosData)
}
})
​
})
}
Any Idea how to make this asynchronous? I tried using the async npm package but was having issue with getting any type of response back from the async.map(array, function (result,callback) {// Was null here} ). As it is right now it is returning an empty object and then logging my data afterwards Thanks to all who help! :)
Have you tried converting the items in the array you're mapping into promises? Then, using something like Promise.all (see below)? I also went ahead and moved a few items around to make it easier for me to read.
const getAdditionalInfosPromise = (ProgramID, LeadId) => {
return new Promise((resolve, reject) => {
connection.query(`SELECT * FROM crm_additionalLeadInfoFields WHERE ProgramID= ${ProgramID};`, (error, response) => {
if (error) reject(console.error(error, 'SQL ERROR IN GETADDITIONALINFOPROMISE'));
let getAdditionalInfosData = [];
const getAdditionalInfos = response;
const allPromises = getAdditionalInfos.map((data, i) => {
if (data.DisplayName !== 'Dealer Name') {
const tableNameData = data.TableName === 'leads' ? `WHERE id= ${LeadId};` : `Where LeadId = ${LeadId};`;
const columnName = data.ColumnName.replace(/ +/g, "")​
return new Promise((resolve, reject) => {
connection.query(`SELECT ${columnName} as pertinentValue FROM ${data.TableName} ${tableNameData}`, (err, res) => {
if (err) console.error(err, 'MY SQL ERR IN GETADDITIONALINFOSPROMISE');
console.log('within the select query', data);
const pertinentValue = res[0].pertinentValue​
resolve({
'id': `additionalItem${i}`,
'label': data.DisplayName,
'value': `${pertinentValue !== null ? pertinentValue : ''}`,
'class': ''
})​
})
})
}
reject({});
});
console.log(allPromises)
resolve(getAdditionalInfosData)
})​
})
}
Then you can do something like:
Promise.all(allPromises).then(function(values) {
console.log(values);
// do anything you need with your data here
});

React Native - Return value from event listener in a promise

This has more to do with promises and async/await than the package I am using but in any event, the package is react-native-background-upload and I have an issue with getting back the response from inside of the 'completed' event listener. I think my understanding of how these promises resolve might be wanting but I need to return the response from the server after the upload is complete. The example file provided here mostly caters for logging to the console, not returning data from the server.
async submit() {
const responsefromUpload = await this.videoUploader().then((data) => data);
console.log(responsefromUpload);
}
from which I call the function below
videoUploader() {
const { video } = this.state;
video.uri = Platform.OS == 'android' ? video.uri.replace('file://', '') : video.uri;
const options = {
url: 'https://upload.wistia.com',
path: video.uri,
method: 'POST',
field: 'file',
type: 'multipart',
};
return Upload.startUpload(options).then((uploadId) => {
console.log('Upload started');
Upload.addListener('progress', uploadId, (data) => {
console.log(`Progress: ${data.progress}%`);
});
Upload.addListener('error', uploadId, (data) => {
console.log(`Error: ${data.error}%`);
});
Upload.addListener('cancelled', uploadId, (data) => {
console.log('Cancelled!');
});
Upload.addListener('completed', (data) => {
// data includes responseCode: number and responseBody: Object
console.log('Completed!');
return data.resporesponseBody; // this return doesn't work
});
}).catch((err) => {
console.log('Upload error!', err);
});
}
Any assistance will be greatly appreciated.
wrap the code in .then((uploadId) => { ... } in a "new Promise" - resolve the promise in completed handler ... resolve(data.resporesponseBody);
i.e.
videoUploader() {
const {video} = this.state;
video.uri = Platform.OS == 'android' ? video.uri.replace('file://', '') : video.uri;
const options = {
url: 'https://upload.wistia.com',
path: video.uri,
method: 'POST',
field: 'file',
type: 'multipart',
};
return Upload.startUpload(options)
.then(uploadId => new Promise((resolve, reject) => {
console.log('Upload started');
Upload.addListener('progress', uploadId, data => {
console.log(`Progress: ${data.progress}%`);
});
Upload.addListener('error', uploadId, data => {
console.log(`Error: ${data.error}%`);
reject(data);
});
Upload.addListener('cancelled', uploadId, data => {
console.log('Cancelled!');
reject(data); //?
});
Upload.addListener('completed', data => {
// data includes responseCode: number and responseBody: Object
console.log('Completed!');
resolve(data.responseBody);
});
}))
.catch(err => {
console.log('Upload error!', err);
});
}
you can the wrap startUpload .then block code inside a Promise and resolve it on the completion of file upload.
function videoUploader() {
const { video } = this.state;
video.uri = Platform.OS == 'android' ? video.uri.replace('file://', '') : video.uri;
const options = {
url: 'https://upload.wistia.com',
path: video.uri,
method: 'POST',
field: 'file',
type: 'multipart'
};
return Upload.startUpload(options).then(uploadId => {
const uploadCompletionPromise = new Promise(function(resolve, reject) {
Upload.addListener('progress', uploadId, data => {
console.log(`Progress: ${data.progress}%`);
});
Upload.addListener('error', uploadId, data => {
console.log(`Error: ${data.error}%`);
});
Upload.addListener('cancelled', uploadId, data => {
console.log('Cancelled!');
});
Upload.addListener('completed', data => {
console.log('Completed!');
resolve(data.resporesponseBody);
});
});
return uploadCompletionPromise
.then(response => ({ success: true, data: response }))
.catch(error => ({ success: false, error }));
}).catch((error) => {
console.log('Upload error!', error);
return { success: false, error }
});
}

How to detect FileReader load end?

I have the following model with a file reading function. However, the reading is done after the next piece of code. Why and how can I get it to return the read content of the file?
TreeContainer = Backbone.Model.extend({
elementCount: 0,
file: '',
object: {jj: "kk"},
type: "input",
parent: d3.select("#canvas"),
initialize: function () {
var result = this.readFile();
for (var i = 0; i < 200; i++) {
console.log(i); //this is resulted before the readFile content
}
},
readFile: function () {
var model = this;
// display text
if (this.get('file').name.endsWith(".json") || this.get('file').type === "application/json") {
var reader = new FileReader();
reader.onload = function (e) {
//parseJSON
var text = e.target.result;
var data = JSON.parse(text);
model.set('object', data);
console.log(data);
return data;
};
reader.readAsText(this.get('file'));
}
}
});
You have fallen for the async for loop problem
You can only loop over all files after you have read all the content
Here is a example using Screw-FileReader
initialize: function(){
this.readFile().then(arr => {
for (let json of arr) {
console.log(json) //this is resulted before the readFile content
}
})
},
readFile: function() {
function isJSON(file) {
return file.name.toLowerCase().endsWith('.json') ||
file.type === 'application/json'
}
return Promise.all(
Array.from(input.files)
.filter(isJSON)
.map(file => file.json())
)
}
Alternetive is to wrap FileReader in a Promise
return new Promise(resolve => {
reader.onload = function (e) {
var text = e.target.result
var data = JSON.parse(text)
model.set('object', data)
resolve(data)
}
})
or send in a callback
initialize: function(){
this.readFile(json => {
json
})
},
readFile: function(resolve) {
resolve(data)
}

Categories