Dear all,
I am quite new to the community, so hello everyone. I need help or advise on how to achieve the following. I am creating a form where I am uploading a pdf document and a picture into two separate collections using ostrio:files (the code to upload is done client side as I followed their documentation). The resulting id of each is then to be added to an object which is then passed to a Meteor.call(). I am using check() in my Meteor method to ensure that data is proper. The problem is one of a race condition. The files get uploaded, but the Meteor.call() gets fired up before an id could be returned from the file upload, and since checking that id to be a String, the whole Meteor.call() fails. The files are however successfully saved in their respective collections, and the ids for these files are successfully added to my data object. Below is my code, and further below a screenshot of the browser console. My knowledge of promises and async/await functions is very limited. Any help, guidance in how to resolve this situation is highly appreciated.
Meteor method
Meteor.methods({
// Method to add a doc
'metCollBookShelf.addDoc': function(docData) {
console.log(docData);
if(Roles.userIsInRole(this.userId, ['superadmin', 'bookShelfAdmin'])) {
// Check all inputs
// Check directly all mandatory fields
check(docData.title, String);
check(docData.desc, String);
check(docData.mainAuthor, String);
check(docData.bookContentsId, String);
// Conditionally check inputs if there is a value
if(docData.aboutAuthor) { check(docData.aboutAuthor, String); }
if(docData.coAuthors) { check(docData.coAuthors, Array); }
if(docData.publisher) { check(docData.publisher, String); }
if(docData.publicationDate) { check(docData.publicationDate, Date); }
if(docData.language) { check(docData.language, String); }
if(docData.ISBN) { check(docData.ISBN, String); }
if(docData.bookCoverId) { check(docData.bookCoverLocId, String); }
const docOwner = this.userId;
// Insert Doc
// On insertion, grab the docId for logging
const docId = CollBookShelf.insert(docData);
// Event Logging
const logEventData = docData;
logEventData.docId = docId;
logEventData.docOwner = docOwner;
Meteor.call('metCollAppLog.addDoc', 'x010101', 'addDoc', logEventData, function(err) {
if (err) {
throw new Meteor.Error('ErrorLog', TAPi18n.__('gen.msg.errorLogging'));
}
});
} else {
throw new Meteor.Error('Unauthorised', TAPi18n.__('gen.msg.unauthorisedAddDoc'));
}
},
Ostrio:files collection definition
export const MFCollBookShelfContents = new FilesCollection({
collectionName: 'mfCollBooKShelfContents',
allowClientCode: false,
onBeforeUpload(file) {
if(Roles.userIsInRole(this.userId, ['superadmin', 'bookShelfAdmin'])) {
return true;
}
return 'You are not authorised to upload files';
},
onBeforeRemove(file) {
if(Roles.userIsInRole(this.userId, ['superadmin', 'bookShelfAdmin'])) {
return true;
}
return 'You are not authorised to upload files';
}
});
// Deny interactions from client
if (Meteor.isServer) {
MFCollBookShelfContents.denyClient();
}
Form submit event (BlazeJs)
// =================================================== //
// Submit Document //
// =================================================== //
'submit #blz-form-book-shelf-add'(evt) {
evt.preventDefault();
// Create docData object
const docData = {};
// Grab value and put in the object. Object property name should match db field names. Refer to associated method.
docData.title = evt.target.bookTitle.value;
docData.desc = evt.target.bookDesc.value;
// Insert the cover first because the contents file may be bigger
const fileObj_Cover = evt.target.blz_inp_book_cover;
// console.log(fileObj_Cover);
if (!fileObj_Cover.files[0]) {
// docData.bookCoverLoc = '/images/default/noBookCover.jpg';
docData.bookCoverId = '';
} else if (fileObj_Cover.files && fileObj_Cover.files[0]) {
const uploadCover = MFCollBookShelfCovers.insert({
file: fileObj_Cover.files[0],
streams: 'dynamic',
chunkSize: 'dynamic'
}, false);
uploadCover.on('start', function () {
});
uploadCover.on('end', function (err, fileObj) {
if (err) {
throw new Meteor.Error('Upload error!', 'There was an error while uploading your file.');
} else {
docData.bookCoverId = fileObj._id;
console.log(fileObj);
console.log(docData);
}
});
uploadCover.start();
}
// Upload book contents
const fileObj_Contents = evt.target.blz_inp_book_contents;
if (fileObj_Contents.files && fileObj_Contents.files[0]) {
const uploadContents = MFCollBookShelfContents.insert({
file: fileObj_Contents.files[0],
streams: 'dynamic',
chunkSize: 'dynamic'
}, false);
uploadContents.on('start', function () {
});
uploadContents.on('end', function (err, fileObj) {
if (err) {
throw new Meteor.Error('Upload error!', 'There was an error while uploading your file.');
} else {
docData.bookContentsId = fileObj._id;
console.log(fileObj);
console.log(docData);
}
});
uploadContents.start();
}
// Insert the Doc
Meteor.call('metCollBookShelf.addDoc', docData, function (err, res) {
if (err) {
toastr.error('There was an error saving your information.', 'Error.');
} else if(res){
toastr.success('Your publication has been stored on the bookshelf.', 'Well done!')
}
});
Console in chrome. You will see that the data object does not include the file ids, but gets added later after the file has been successfully uploaded. But then, it’s too late since Meteor.call has already been fired.