Global variable not updated - node.js

I have a problem in this code.
I am trying to parse an excel file but cannot make the parent relationship.
Parent records are created successfully, child records are also created, but parent_id is always 0
let parent_id = 0; // this var not updated!!!
let workbook = new Excel.Workbook()
workbook = await workbook.xlsx.readFile(location)
workbook.worksheets[0].eachRow(async (row) => {
console.log(parent_id) // each time 0,0,0,0
const name = row.getCell(1).value
const obj = {
name,
number: row.getCell(2).value,
parent_id: name ? null : parent_id
}
if(obj.number && Number(obj.number) == obj.number){
const node = await LeadNode.create(obj)
if(name){
parent_id = node.id
console.log(parent_id) // work here 1,2,3,4,5
}
}
});
db screen

The problem is caused by .eachRow() not awaiting for your callback to finish.
If you somehow make the callback not async, it would work. This means that you'll have to call this await LeadNode.create(obj) outside the callback.
Possible solution: create an array of obj, then iterate over them and call the await LeadNode(...). Something like:
let parent_id = 0; // this var not updated!!!
let workbook = new Excel.Workbook()
workbook = await workbook.xlsx.readFile(location)
const objs = [];
workbook.worksheets[0].eachRow((row) => {
console.log(parent_id) // each time 0,0,0,0
const name = row.getCell(1).value
const obj = {
name,
number: row.getCell(2).value,
}
objs.push(obj);
});
for (const obj of objs){
if(obj.number && Number(obj.number) == obj.number){
obj.parent_id = obj.name ? null : parent_id
const node = await LeadNode.create(obj)
if(name){
parent_id = node.id
console.log(parent_id) // work here 1,2,3,4,5
}
}
}

Related

Properties fragment query has no effect

I am using the Node.js API provided by MarkLogic to read values from the URI lexicon using the following code
const marklogic = require("marklogic");
const dbConn = require("../config/ml-connections");
const dbRead = marklogic.createDatabaseClient(dbConn.restReader);
const dbWrite = marklogic.createDatabaseClient(dbConn.restWriter);
const qb = marklogic.queryBuilder;
const vb = marklogic.valuesBuilder;
let termOption: string = 'exact';
let status: string = 'active';
let startDateTime = '2022-08-01T00:00:00Z';
let endDateTime = '2022-09-01T00:00:00Z';
let start = 1;
let end = 10;
if (status === 'all') {
termOption = 'wildcarded';
status = '*';
}
dbRead.values.read(
vb.fromIndexes(vb.uri())
.where(
qb.and(
qb.value('status', status, qb.termOptions(termOption)),
qb.propertiesFragment(
qb.and(
qb.range(qb.qname('prop:last-modified'), qb.datatype('dateTime'), '>=', startDateTime),
qb.range(qb.qname('prop:last-modified'), qb.datatype('dateTime'), '<=', endDateTime)
)
)
)
)
.slice(start, end)
.withOptions({values: ['descending']})
)
.result()
.then(function (resultObject) {
let uris: string[] = [];
if (resultObject['values-response'].tuple) {
for (const row of resultObject['values-response'].tuple) {
uris.push(row['distinct-value'][0]);
}
}
return uris;
})
This code works, but the properties fragment query here has no effect on the result of the program. What could be wrong?
PS: Ignore syntax and obvious coding errors, if any, as the code works. MarkLogic version 10.0-9.4.

how to save data downloaded in a loop to json

I have a problem. I got one to save the data called in a loop to an empty json. It's about "eventsPolygon". One args with index 0 will have to be written to JSONA. How to do it?
async function main() {
console.log("Start checking rewards")
const currentBlockNumberPolygon = await maticProvider.getBlockNumber() - 1
const currentBlockNumberBsc = await bscProvider.getBlockNumber() - 1
const oldestBlockNumberPolygon = 22939848
const oldestBlockNumberBsc = 13763979
const eventFilterPolygon = Missions.filters.RewardToPay()
const eventFilterBsc = Rewards.filters.RewardPayed()
let eventsPolygon = []
let eventsBsc = []
for(let i = oldestBlockNumberPolygon; i < currentBlockNumberPolygon-10000; i+=10000) {
const eventsLoop = await Missions.queryFilter(eventFilterPolygon, i, i+10000)
eventsPolygon = eventsPolygon.concat(eventsLoop)
console.log(i)
}
//for(let i = oldestBlockNumberBsc; i < currentBlockNumberBsc-10000; i+=10000) {
//const eventsLoop = await Rewards.queryFilter(eventFilterBsc, i, i+10000)
// eventsBsc = eventsBsc.concat(eventsLoop)
//console.log(i)
//}
console.log('a')
}
when iterating if your certain that you need the zero index you could just make a condition inside your loop, if(i == 0){wanted action}

NodeJS: API not returning response after loop execution is completed although it returns inside the loop

In my api creation there is one strange issue I come across that it does not return the final output after successful execution of the nested forEach loop.
But it consoles the result inside the loop execution. So, what is the thing that I am missing to get the response ?
repository.ts
export class ItemInventoryRepository {
public async getItemsData(itemId) {
try {
let mainItemList = [];
var subList;
let mainQuery = `SELECT table1.id as colOneId,
FROM table1
WHERE table1.item_id = ${pool.escape(itemId)} AND option_id = 1`
let optOneRecords = await pool.query(mainQuery);
if(optOneRecords.length > 0) {
optOneRecords.forEach(async element => {
let id = element.colOneId
let subQuery = `SELECT table1.id as colTwoId,
FROM table1
LEFT JOIN table2 ON table1.id = table2.col_two_id
WHERE table1.item_id = ${pool.escape(itemId)}
AND option_id = 2
AND table2.col_one_id = ${id}`
subList = await pool.query(subQuery);
await mainItemList.push({
"colOneId": element.colOneId,
"subList": subList
})
console.log("inSideLoopData :: ", mainItemList)
});
console.log("outSideLoopData :: ", mainItemList)
}
return mainItemList;
} catch(error) {
throw error
}
}
}
In the below attached terminal screen shot the outSideLoopData consoles first and then inSideLoopData consoles.
Postman response where getting the actual response is returning empty array.
controller.ts
public async getItemById(req: Request, res: Response, next: NextFunction) {
try{
let repository = new ItemInventoryRepository;
let result = await repository.getItemsData(req.params.id);
res.status(200).json({data: result})
} catch(error) {
next(error)
}
}
---- await dosen't work in forEach loop ------
optOneRecords.forEach(async element => {
let id = element.colOneId
let subQuery = `SELECT table1.id as colTwoId,
FROM table1
LEFT JOIN table2 ON table1.id = table2.col_two_id
WHERE table1.item_id = ${pool.escape(itemId)}
AND option_id = 2
AND table2.col_one_id = ${id}`
subList = await pool.query(subQuery); // await dosen't work in forEach
await mainItemList.push({ loop
"colOneId": element.colOneId,
"subList": subList
})
console.log("inSideLoopData :: ", mainItemList)
});
Pleae use for loop
for(const element of optOneRecords){
let id = element.colOneId
let subQuery = `SELECT table1.id as colTwoId,
FROM table1
LEFT JOIN table2 ON table1.id = table2.col_two_id
WHERE table1.item_id = ${pool.escape(itemId)}
AND option_id = 2
AND table2.col_one_id = ${id}`
subList = await pool.query(subQuery);
await mainItemList.push({ // await dosen't work in forEach loop
"colOneId": element.colOneId,
"subList": subList
})
console.log("inSideLoopData :: ", mainItemList)
}

How to properly assign json data into a javascript class?

I have trouble to make functions inside for loop to run synchrosely
Inside EmployeePrint.js
fillData(item, taxObj) {
let fullName = item.getFullName();
let timePeriod = item.getTimePeriod();
let grossIncome = item.getGrossIncome();
let incomeTax = item.getIncomeTax(taxObj);
let netIncome = item.getNetIncome();
let mySuper = item.getSuper();
this.name = fullName;
this.payPeriod = timePeriod;
this.grossIncome = grossIncome;
this.incomeTax = incomeTax;
this.netIncome = netIncome;
this.mySuper = mySuper;
return this;
}
item is employee object. It has some methods and I run it inside fillData
async print(arr, fileName, taxObj) {
let buf = [];
arr.map(async (item) => {
let ep = new EmployeePrint();
ep = ep.fillData(item, taxObj);
buf.push(ep);
});
return await this.printPromise(buf, fileName);
}
After the fill data, I push employee object into array for printing later.
My issue is that I don't know how to make sure all funcs completed in each iteration.
Full project here: https://github.com/kenpeter/mb_new/tree/extend_base
For starters, you should call forEach and push into buf, or just use the returned value from map instead of even creating buf.
Second, you should use a for loop if you want to await inside a loop (not map or forEach) or simply map to promises and await all.
async print(arr, fileName, taxObj) {
let buf = [];
const promises = arr.map((item) => {
let ep = new EmployeePrint();
ep = ep.fillData(item, taxObj);
buf.push(ep);
return this.printPromise(buf, fileName);
});
await Promise.all(promises)
}

How to Mock Subsonic ExecuteReader method?

I have a method that calls stored procedure and returns the data after executing DataReader.
I am trying to test the method using mock. I am not sure how to return value?
Anyone did this? Appreciate your responses.
Here is my code:
// Call the StoredProcedure
public List<string> GetCompletedBatchList(int fileId)
{
List<string> completedBatches = new List<string>();
StoredProcedure sp = new StoredProcedure("GetDistributedBatches", this.dataProvider);
sp.Command.AddParameter("FileID", fileId, DbType.Int32, ParameterDirection.Input);
sp.Command.AddParameter("Result", null, DbType.Int32, ParameterDirection.InputOutput);
using (var rdr = sp.ExecuteReader())
{
while (rdr != null && rdr.Read())
{
if (rdr[0] != null)
{
completedBatches.Add(rdr[0].ToString());
}
}
}
return completedBatches;
}
Here is the Test Method:
[Test]
public void Can_get_completedBatches()
{
var file = new File() { FileID = 1, DepositDate = DateTime.Now };
repo.Add<File>(file);
CompletedBatches completedBatches = new CompletedBatches(provider.Object);
//Here I am not sure how to Return
provider.Setup(**x => x.ExecuteReader(It.IsAny<QueryCommand>())).Returns** =>
{
cmd.OutputValues.Add(0);
});
var completedBatchesList = completedBatches.GetCompletedBatchList(file.FileID);
Assert.AreEqual(0, completedBatchesList.Count());
}
If you want to create a DataReader of a certain shape and size then I suggest you look at DataTable.CreateDataReader DataTable.CreateDataReader. You can then setup the ExecuteReader in your example to return this datareader.
The following link helped me...
How to mock an SqlDataReader using Moq - Update
I used MockDbDataReader method to mock the data
[Test]
public void Can_get_completedBatches_return_single_batch()
{
var date = DateTime.Now;
var file = new File() { FileID = 202, DepositDate = DateTime.Now };
var batch1 = new Batch() { FileID = 202, BatchID = 1767, LockboxNumber = "1", IsLocked = true, LockedBy = "testUser" };
var transaction1 = new Transaction() { BatchID = 1767, TransactionID = 63423, CheckAmount = 100.0 };
var distribution1 = new Distribution() { TransactionID = 63423, InvoiceNumber = "001", Amount = 100.0, DateCreated = date, DateModified = date, TransType = 2 };
repo.Add<File>(file);
repo.Add<Batch>(batch1);
repo.Add<Transaction>(transaction1);
repo.Add<Distribution>(distribution1);
CompletedBatches completedBatches = new CompletedBatches(provider.Object);
provider.Setup(x => x.ExecuteReader(It.IsAny<QueryCommand>())).Returns(MockDbDataReader());
var completedBatchesList = completedBatches.GetCompletedBatchList(202);
Assert.AreEqual(1, completedBatchesList.Count());
}
// You should pass here a list of test items, their data
// will be returned by IDataReader
private DbDataReader MockDbDataReader(List<TestData> ojectsToEmulate)
{
var moq = new Mock<DbDataReader>();
// This var stores current position in 'ojectsToEmulate' list
int count = -1;
moq.Setup(x => x.Read())
// Return 'True' while list still has an item
.Returns(() => count < ojectsToEmulate.Count - 1)
// Go to next position
.Callback(() => count++);
moq.Setup(x => x["BatchID"])
// Again, use lazy initialization via lambda expression
.Returns(() => ojectsToEmulate[count].ValidChar);
return moq.Object;
}

Resources