I am trying to make use of async series correctly but unable to figure how exactly i need to send the data into it for it to move forward with each post request in exactly the same way as it is getting data.
I have an excel file in which i am getting multiple statuses to mark against each ids that are present in the first column. Also there are statuses that need to be deleted and deleting part is not that difficult and i was able to do it without making use of async series but for posting new records I need to make use of async series. The records to be posted are under the columns 'Mark Status 1', 'Mark Status 1 Date', 'Mark Status 2' and so on. So i wrote a script to fetch the records using a for loop and send them off to a function and that function is responsible for modelling data for asyn series. Inside the markStatusCall function if i put a forEach loop and run the call the sequence of the statuses become wrong. It should be like 'Marks Status 1 value', 'Mark Status 2 value' and so on.
I am attaching my code here, please take a look below and to see the excel file here
const Excel = require("exceljs");
const axios = require("axios").default;
const https = require("https");
const _async = require("async");
const instance = axios.create({
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
});
const returnedId = "5dd7fa20dcfa9600152cc2de";
const deliveredId = "5dd7fa20dcfa9600152cc2d3";
const returnedByVendorId = "5de7c418362e13001212f238";
const returnedToVendor = "5eb2ebfe02987816aad14269";
const atSwyftWarehouse = "5dd7fa20dcfa9600152cc2d8";
const reAttempted = "5e6ea5d87aa7bb6d726b2bbc";
const requestToReattempt = "5ee134230138634c27a6e1da";
const dispatched = "5dd7fa20dcfa9600152cc2e2";
const parcelAssignedDelivery = "5dd7fa20dcfa9600152cc2e3";
const cancelledByVendor = "5de7c418362e13001212f238";
var workbook = new Excel.Workbook();
workbook.xlsx.readFile("./1.xlsx").then(async function () {
// use workbook
var worksheet = workbook.getWorksheet("Interim");
const parcelIds = [];
const status = [];
const date = [];
var data = [];
var finalData = [];
for (let i = 2; i <= 10; i++) {
worksheet.getRow(i).eachCell((cell, index) => {
if (cell.value !== "") {
worksheet.getColumn(index).eachCell((colCell, indexing) => {
if (indexing === 1) {
if (colCell.value === "Delete Status 1") {
deleteData(i, cell);
} else if (colCell.value === "Delete Status 2") {
deleteData(i, cell);
} else if (colCell.value === "Delete Status 3") {
deleteData(i, cell);
} else if (colCell.value === "Delete Status 4") {
deleteData(i, cell);
} else if (colCell.value === "Delete Status 5") {
deleteData(i, cell);
} else if (colCell.value === "Mark Status 1") {
markData(i, index, cell);
} else if (colCell.value === "Mark Status 2") {
markData(i, index, cell);
} else if (colCell.value === "Mark Status 3") {
markData(i, index, cell);
} else if (colCell.value === "Mark Status 4") {
markData(i, index, cell);
} else if (colCell.value === "Mark Status 5") {
markData(i, index, cell);
}
}
});
}
});
}
function markData(i, index, cell) {
let row = worksheet.getRow(i);
let date = row.getCell(index + 1).value;
let parcelId = row.getCell(1).value;
if (cell.value !== "" && date !== "") {
let statusId =
cell.value === "At Swyft Warehouse"
? atSwyftWarehouse
: cell.value === "Dispatched"
? dispatched
: cell.value === "Reattempted"
? reAttempted
: cell.value === "Delivered"
? deliveredId
: cell.value === "Cancelled"
? returnedId
: cell.value === "Request for Reattempt"
? requestToReattempt
: cell.value === "Parcel Assigned"
? parcelAssignedDelivery
: cell.value === "Cancelled by Vendor"
? cancelledByVendor
: deliveredId;
console.log(parcelId, statusId, date);
addStatus(parcelId, statusId, date);
}
}
// Need help from here
function addStatus(parcelId, statusId, date) {
let values = {
parcelId: parcelId,
statusRepositoryId: statusId,
createdAt: date,
updatedByScript: true,
};
data.push(values);
}
finalData.push(() => markStatusCall(data));
_async.series(finalData, (err, data) => {
if (err) {
console.log(err);
}
});
async function markStatusCall(values) {
console.log(values, "Came here");
values.forEach((data) => {
try {
let response = await instance.post(
"https://api.myDomain.com:3000/api/ParcelStatuses",
{
parcelId: data.parcelId,
statusRepositoryId: data.statusRepositoryId,
createdAt: data.createdAt,
updatedByScript: data.updatedByScript,
}
);
console.log("Updated");
} catch (err) {
console.log("here");
console.error(err);
}
})
}
Would something like this work:
async function markStatusCall(values) {
console.log(values, "markStatusCall begin");
function loop(values, index) {
console.log('loop at index', index);
const data = {
parcelId: data.parcelId,
statusRepositoryId: data.statusRepositoryId,
createdAt: data.createdAt,
updatedByScript: data.updatedByScript};
try {
let response = await instance.post("https://api.myDomain.com:3000/api/ParcelStatuses", data);
console.log("Updated");
if (index <= (values.length -1)) {
loop(index + 1);
} else {
return;
}
} catch (err) {
console.log("Error");
console.error(err);
}
}
await loop(values, 0);
}
The idea is that the loop waits for each POST request before moving onto the next item in the values array. (might be some typos in the code above).
See this example on codepen
Try doing something like this
function markStatusCall(values) {
let promises=[];
values.forEach((data) => {
const promise = instance.post(
"https://api.myDomain.com:3000/api/ParcelStatuses",
{
parcelId: data.parcelId,
statusRepositoryId: data.statusRepositoryId,
createdAt: data.createdAt,
updatedByScript: data.updatedByScript,
});
return promises.push(promise);
})
Promise.all(promises).then(() => {
console.log('success')
}).catch(error => {
console.error(error.message)
});
}
This can still not ensure your order but it's highly recommended to use promises than async-await
The order of response will follow your order only if the latency for all the responses are same
Related
I have a school management project, and in the "UploadMarks" tab, when I click on the button to send the form with the students attendance, the error "Error in uploading marks Expected string but received array", I've heard that it's a problem with lib validator for validations, but I canĀ“t resolve this error.
uploadMarks:
const Validator = require('validator');
const isEmpty = require('./is-empty');
const validateFacultyUploadMarks = (data) => {
let errors = {}
data.subjectCode = !isEmpty(data.subjectCode) ? data.subjectCode : '';
data.exam = !isEmpty(data.exam) ? data.exam : '';
data.totalMarks = !isEmpty(data.totalMarks) ? data.totalMarks : '';
if (Validator.isEmpty(data.subjectCode)) {
errors.subjectCode = 'Subject Code field is required';
}
if (Validator.isEmpty(data.exam)) {
errors.exam = 'Exam field is required';
}
if (Validator.isEmpty(data.totalMarks)) {
errors.totalMarks = 'Total marks field is required';
}
return {
errors,
isValid: isEmpty(errors)
};
}
module.exports = validateFacultyUploadMarks
teacherController:
uploadMarks: async (req, res, next) => {
try {
const { errors, isValid } = validateFacultyUploadMarks(req.body);
// Check Validation
if (!isValid) {
return res.status(400).json(errors);
}
const {exam, totalMarks, marks, department, year } = req.body
const isAlready = await Mark.find({ exam, department})
if (isAlready.length !== 0) {
errors.exam = "You have already uploaded marks of given exam"
return res.status(400).json(errors);
}
for (var i = 0; i < marks.length; i++) {
const newMarks = await new Mark({
student: marks[i]._id,
exam,
department,
marks: marks[i].value,
totalMarks
})
await newMarks.save()
}
res.status(200).json({message:"Marks uploaded successfully"})
}
catch (err) {
console.log("Error in uploading marks",err.message)
}
},
is-Empty:
const isEmpty = value =>
value === undefined ||
value === null ||
(typeof value === 'object' && Object.keys(value).length === 0) ||
(typeof value === 'string' && value.trim().length === 0);
module.exports = isEmpty;
I'm trying to build a citation generator from json in an API with data about images, stored in key-value pairs. I can get the data to return to the screen, but it always includes undefined in the citation. Sample manifest returns undefined as the creator since that isn't listed in this particular record. How can I keep any undefined value from being returned? I've tried changing the forEach to map, filtering at allMetadata by string length, using if !== undefined at insertCitation, and versions of those in different spots in the code.
EDIT: updated to provide full code, including print to page
(function () {
'use strict';
const buildCitation = {
buildMetadataObject: async function (collAlias, itemID) {
let response = await fetch('/iiif/info/' + collAlias + '/' + itemID + '/manifest.json');
let data = await response.json()
let allMetadata = data.metadata
let citationData = {};
allMetadata.forEach(function (kvpair) {
if (kvpair.value == undefined) {
return false;
} else if (kvpair.label === 'Title') {
citationData.itemTitle = kvpair.value;
} else if (kvpair.label === 'Creator') {
citationData.itemCreator = kvpair.value;
} else if (kvpair.label === 'Repository') {
citationData.itemRepository = kvpair.value;
} else if (kvpair.label === 'Collection Name') {
citationData.itemCollection = kvpair.value;
} else if (kvpair.label === 'Owning Institution') {
citationData.itemOwning = kvpair.value;
} else if (kvpair.label === 'Date') {
citationData.itemDate = kvpair.value;
} else if (kvpair.label === 'Storage Location') {
citationData.itemStorage = kvpair.value;
}
return true;
});
return citationData;
},
insertCitation: function (data) {
var testTitle = data.itemTitle;
console.log(testTitle);
const itemCite = `Citation: "${data.itemTitle}," ${data.itemDate}, ${data.itemCreator}, ${data.itemCollection}, ${data.itemOwning}, ${data.itemStorage}, ${data.itemRepository}.`;
const citationContainer = document.createElement('div');
citationContainer.id = 'citation';
citationContainer.innerHTML = itemCite;
// CHANGED to innerHTML instead of innerText because you may want to format it at some point as HTML code.
if (testTitle) {
document.querySelector('.ItemView-itemViewContainer').appendChild(citationContainer);
}
}
}
document.addEventListener('cdm-item-page:ready', async function (e) {
const citationData = await buildCitation.buildMetadataObject(e.detail.collectionId, e.detail.itemId);
console.log({ citationData });
buildCitation.insertCitation(citationData);
});
document.addEventListener('cdm-item-page:update', async function (e) {
document.getElementById('citation').remove();
const citationData = await buildCitation.buildMetadataObject(e.detail.collectionId, e.detail.itemId);
console.log({ citationData });
buildCitation.insertCitation(citationData);
});
})();
I've simplified your program. The undefined is coming from the fact that there is no item with label Date
const mappings = {
Date: 'itemDate',
Title: 'itemTitle',
Creator: 'itemCreator',
Repository: 'itemRepository',
'Storage Location': 'itemStorage',
'Owning Institution': 'itemOwning',
'Collection Name': 'itemCollection',
}
async function buildMetadataObject(collAlias, itemID) {
let response = await fetch('https://teva.contentdm.oclc.org/iiif/info/p15138coll25/1421/manifest.json');
let data = await response.json()
return data.metadata.reduce(
(acc, { label, value }) => ({ ...acc, [ mappings[label] ]: value }),
{}
)
}
function insertCitation(data) {
var testTitle = data.itemTitle;
const fieldBlackList = ['itemTitle'];
const itemCite = `Citation: "${data.itemTitle}," ${
Object.values(mappings).reduce((acc, cur) => {
if (fieldBlackList.includes(cur)) return acc;
const value = data[cur];
return value ? [...acc, value] : acc
}, []).join(', ')
}.`;
console.log(itemCite);
}
//MAIN PROGRAM
(async() => {
const citationData = await buildMetadataObject();
insertCitation(citationData);
})()
I am trying to return the correct count of messages (length of listMessage) from the function below. I can retrieve correct message Objects into listMessage, but its length is always zero.
checkLastMsg = () =>
{
var groupChatId = null;
var listMessage = [];
// console.log(this.peerUserId);
if (this.hashString(this.currentUserId) <= this.peerUserId)
{
groupChatId = `${this.currentUserId}-${this.peerUserId}`;
}
else
{
groupChatId = `${this.peerUserId}-${this.currentUserId}`;
}
// console.log(groupChatId);
myFirestore
.collection(AppString.NODE_MESSAGES)
.doc(groupChatId)
.collection(groupChatId)
.onSnapshot(
snapshot =>
{
snapshot.docChanges().forEach(change =>
{
if (change.type === AppString.DOC_ADDED)
{
listMessage.push(change.doc.data())
}
})
},
err =>
{
this.props.showToast(0, err.toString())
})
// console.log(listMessage.length);
console.log(listMessage.length);
}
Could anyone check if something is wrong here and how I can fix this issue?
Like everybody said, the code is asynchronous, hence you can make work like this:
checkLastMsg = async () =>
{
var groupChatId = null;
var listMessage = [];
// console.log(this.peerUserId);
if (this.hashString(this.currentUserId) <= this.peerUserId)
{
groupChatId = `${this.currentUserId}-${this.peerUserId}`;
}
else
{
groupChatId = `${this.peerUserId}-${this.currentUserId}`;
}
// console.log(groupChatId);
await myFirestore
.collection(AppString.NODE_MESSAGES)
.doc(groupChatId)
.collection(groupChatId)
.onSnapshot(
snapshot =>
{
snapshot.docChanges().forEach(change => {
if (change.type === AppString.DOC_ADDED) {
listMessage.push(change.doc.data())
}
})
},
err => {
this.props.showToast(0, err.toString())
}
)
// console.log(listMessage.length);
console.log(listMessage.length);
}```
The Firebase code running asynchronously. Try to maybe log the list in the callback function or something like that
I am trying to write a function that takes into account 3 conditions whenever Stores/{storeId}/{departmentId}/{productId} gets triggered and write new data in ref.child('Home').child('Chiep').child(departmentId).child(productId).
1) When there is no data in firestore, I need to fill up all the fields in Realtime DB, by making queries in 2 different firestore's nodes: Stores and Products in order to take their images.
2) When a change is made in Stores node and it comes from the same {storeId}, I just need to update some data without making any additional query.
3) And finally, when a change is made in Stores node and it comes from other {storeId}, I need to make only one query in the Stores node.
exports.homeChiepest = functions.firestore
.document('Stores/{storeId}/{departmentId}/{productId}')
.onWrite((change, context) => {
const storeId = context.params.storeId;
const departmentId = context.params.departmentId;
const productId = context.params.productId;
const ref = admin.database().ref();
// Get an object with the current document value.
// If the document does not exist, it has been deleted.
const document = change.after.exists ? change.after.data() : null;
// Get an object with the previous document value (for update or delete)
const oldDocument = change.before.exists ? change.before.data() : null;
// Prevent infinite loops
if (!change.after.exists) {
console.log('DATA DELETED RETURN NULL');
return null;
}
const newPrice = document.price;
const newTimestamp = document.timestamp;
return ref.child('Home').child('Chiep')
.child(departmentId).child(productId)
.once('value')
.then(dataSnapshot => {
if (dataSnapshot.val() !== null) {
console.log('CHIEP DOES exist');
const oldPrice = dataSnapshot.val().price;
const storeKey = dataSnapshot.val().storeKey;
if (storeId === storeKey) {
console.log('SAME STORE - Change price and timestamp');
var newChiepest = {
timestamp: newTimestamp,
price: newPrice
};
return dataSnapshot.ref.update(newChiepest);
} else {
console.log('OTHER STORE - Verify if price is chieper...');
if (newPrice <= oldPrice) {
console.log('NEW PRICE: '+newPrice+' is chieper than the older one: '+oldPrice);
return change.after.ref.parent.parent.get().then(doc => { // HERE Avoid nesting promises
newStoreImg = doc.data().image;
var newStoreChiep = {
price: newPrice,
storeImg: newStoreImg,
storeKey: storeId,
timestamp: newTimestamp
};
return dataSnapshot.ref.update(newStoreChiep);
});
} else {
console.log('NEW PRICE: '+newPrice+' is mode EXPENSIVE than the older one: '+oldPrice);
}
return null;
}
} else {
console.log('data does NOT exist, so WRITE IT!');
let getStoreData = change.after.ref.parent.parent.get();
let getProductData = admin.firestore().collection('Products').doc('Departments').collection(departmentId).doc(productId).get();
return Promise.all([getStoreData, getProductData]).then(values => { // HERE Avoid nesting promises
const [store, product] = values;
var newHomeChiepest = {
depId: departmentId,
price: newPrice,
prodImg: product.data().image,
prodKey: productId,
storeKey: storeId,
storeImg: store.data().image,
timestamp: newTimestamp
};
return dataSnapshot.ref.set(newHomeChiepest);
});
}
})
.catch(error => {
console.log('Catch error reading Home: ',departmentId ,'/', productId,'; message: ',error);
return false;
});
});
The problem is: different possibilities of querying or not querying another firestore node led me to a warning while uploading the Clound Function, that is:
warning Avoid nesting promises promise/no-nesting
I appreciate any help to refactor this code.
You could use a variable to manage a "shunting", depending on the different cases, as follows (untested):
exports.homeChiepest = functions.firestore
.document('Stores/{storeId}/{departmentId}/{productId}')
.onWrite((change, context) => {
const storeId = context.params.storeId;
const departmentId = context.params.departmentId;
const productId = context.params.productId;
const ref = admin.database().ref();
const document = change.after.exists ? change.after.data() : null;
// Prevent infinite loops
if (!change.after.exists) {
console.log('DATA DELETED RETURN NULL');
return null;
}
const newPrice = document.price;
const newTimestamp = document.timestamp;
let shunting; // <-- We manage the shunting through this variable
let chiepRef;
return ref.child('Home').child('Chiep')
.child(departmentId).child(productId)
.once('value')
.then(dataSnapshot => {
chiepRef = dataSnapshot.ref;
if (dataSnapshot.val() !== null) {
console.log('CHIEP DOES exist');
const oldPrice = dataSnapshot.val().price;
const storeKey = dataSnapshot.val().storeKey;
if (storeId === storeKey) {
shunting = 1
console.log('SAME STORE - Change price and timestamp');
var newChiepest = {
timestamp: newTimestamp,
price: newPrice
};
return chiepRef.update(newChiepest);
} else {
console.log('OTHER STORE - Verify if price is chieper...');
if (newPrice <= oldPrice) {
console.log('NEW PRICE: ' + newPrice + ' is chieper than the older one: ' + oldPrice);
shunting = 2
return change.after.ref.parent.parent.get();
} else {
console.log('NEW PRICE: ' + newPrice + ' is mode EXPENSIVE than the older one: ' + oldPrice);
shunting = 3
return null;
}
}
} else {
console.log('data does NOT exist, so WRITE IT!');
shunting = 4;
let getStoreData = change.after.ref.parent.parent.get();
let getProductData = admin.firestore().collection('Products').doc('Departments').collection(departmentId).doc(productId).get();
return Promise.all([getStoreData, getProductData])
}
})
.then(result => {
if (shunting === 2) {
const newStoreImg = result.data().image;
var newStoreChiep = {
price: newPrice,
storeImg: newStoreImg,
storeKey: storeId,
timestamp: newTimestamp
};
return chiepRef.update(newStoreChiep);
} else if (shunting === 4) {
const [store, product] = result;
const newHomeChiepest = {
depId: departmentId,
price: newPrice,
prodImg: product.data().image,
prodKey: productId,
storeKey: storeId,
storeImg: store.data().image,
timestamp: newTimestamp
};
return chiepRef.set(newHomeChiepest);
} else {
return null;
}
})
.catch(error => {
console.log('may be adapted, function of shunting', error);
return null;
});
});
i have a problem in vuelidate that causing infinite loop. Here's a brief process of my project. I have a datatable, I used Firebase OnSnapShot function to paginate the table. The table have action column that will show a modal when clicked. When i'm updating the value came from the table, vuelidate isUnique functions fires an infinite loop.
P.S I'm detaching the listener before viewing the modal
Output of infinite loop :
Here's my function to load the datatable:
async loadData(firebasePagination) {
// query reference for the messages we want
let ref = firebasePagination.db;
// single query to get startAt snapshot
ref.orderBy(firebasePagination.orderColumn, 'asc')
.limit(this.pagination.rowsPerPage).get()
.then((snapshots) => {
// save startAt snapshot
firebasePagination.start = snapshots.docs[snapshots.docs.length - 1]
// create listener using endAt snapshot (starting boundary)
let listener = ref.orderBy(firebasePagination.orderColumn)
.endAt(firebasePagination.start)
.onSnapshot((datas) => {
if(!datas.empty){
datas.docs.forEach((data, index) => {
//remove duplicates
console.log("here")
firebasePagination.data = firebasePagination.data.filter(x => x.id !== data.id)
//push to the data
firebasePagination.data.push(Object.assign({id : data.id },data.data()))
if(datas.docs.length-1 === index){
//sort
firebasePagination.data.sort((a, b) => (a[firebasePagination.orderColumn] > b[firebasePagination.orderColumn]) ? 1 : -1)
//get the current data
firebasePagination.currentData = this.getCurrentData(firebasePagination)
}
})
}
})
// push listener
firebasePagination.listeners.push(listener)
})
return firebasePagination;
}
Here's my function when clicking the action (Modal):
switch(items.action) {
case 'edit':
//detaching listener
this.firebasePagination.listeners.forEach(d => {
d()
});
items.data.isEdit = true;
this.clickEdit(items.data);
break;
}
}
Here's my isUnique function:
validations: {
department: {
name: {
required,
async isUnique(value){
if(value.trim() === ''){
return false;
}
if(strictCompareStrings(this.departmentName, value)){
this.departmentError.isActive = true;
this.departmentError.isValid = true;
return true;
}
const result = await checkIfUnique(DB_DEPARTMENTS, {nameToLower : this.department.name.toLowerCase()});
console.log("GOES HERE")
if(!result.isValid){
result.errorMessage = result.isActive ?
'Department already exists.' : 'Department has been archived.';
}
this.departmentError = Object.assign({}, result);
return this.departmentError.isValid;
}
}
}
}
Here's my checkUnique function :
export const checkIfUnique = (db, nameObj, isTrim = true) => {
return new Promise(resolve => {
const nameObjKey = Object.keys(nameObj)[0];
const name = isTrim ? nameObj[nameObjKey].replace(/\s+/g,' ').trim().toLowerCase() : nameObj[nameObjKey].trim();
db().where(nameObjKey, '==', name).get()
.then((doc) => {
let result = {isActive: false, isValid: true, errorMessage: ''};
if(!doc.empty){
result.isActive = doc.docs[0].data().isActive;
result.isValid = false;
}
resolve(result);
})
});
};
Looked into another example of using isUnique from here and considered that you might have to return the Promise itself from the isUnique itself.
isUnique(value) {
if (value === '') return true
return new Promise((resolve, reject) => {
yourQueryMethod(`....`)
.then(result => resolve(result))
.catch(e => reject(false));
})
}
But then again, we still have an open issue regarding Infinite loop when using a promise-based validate #350.