Nodejs & TypeORM Transaction Confusion - javascript

I have confusion and I couldn't resolve it from the TypeORM documents.
Here is my function that is used as request handler and it is using transaction taken from getManager()
static makeTransaction = async (req: Request, res: Response) => {
try {
// check if the account ids are same
const { senderAccountId, receiverAccountId, amount } = req.body;
let senderAccount = await findEntityById(getRepository(Account), senderAccountId);
let receiverAccount = await findEntityById(getRepository(Account), receiverAccountId);
senderAccount.balance -= amount;
receiverAccount.balance += amount;
await validateOrReject(senderAccount);
await validateOrReject(receiverAccount);
const tempEntity = MyEntity.create({
amount,
receiverAccount: receiverAccount,
senderAccount: senderAccount,
});
await validateOrReject(tempEntity);
const accounts = await getManager().transaction(async transactionManager => {
await transactionManager.save(senderAccount);
await transactionManager.save(receiverAccount);
return transactionManager.save(tempEntity);
});
return res.status(200).json({
error: null,
data: true,
});
} catch (e) {
return res.status(400).json({ error: "Transaction wasn't successfull" });
}
};
So when I put a catch function for the last saving (only it fails and first 2 of them runs correctly), it gives "Query runner already released. Cannot run queries anymore" error.
When I change the flow like this, it works and I really wonder the reason because in documents there are saving operations for different entity types and this is what I'm trying to do too.
static makeTransaction = async (req: Request, res: Response) => {
try {
// check if the account ids are same
const { senderAccountId, receiverAccountId, amount } = req.body;
let senderAccount = await findEntityById(getRepository(Account), senderAccountId);
let receiverAccount = await findEntityById(getRepository(Account), receiverAccountId);
senderAccount.balance -= amount;
receiverAccount.balance += amount;
await validateOrReject(senderAccount);
await validateOrReject(receiverAccount);
const tempEntity = MyEntity.create({
amount,
receiverAccount,
senderAccount,
});
await validateOrReject(tempEntity);
const transactionRes = await getManager().transaction(async transactionManager => {
return transactionManager.save([tempEntity, senderAccount, receiverAccount]);
});
return res.status(200).json({
error: null,
data: transactionRes[0],
});
} catch (e) {
console.log(e);
return res.status(400).json({ error: "Transaction wasn't successfull" });
}
};
I want to learn the reason for that and any kind of idea or help is appreciated, thank you in advance.

Related

Investigating an issue with my Uniswap tokens scraper that errors out after 7 requests to the Graph API

I'm making a scraper that will grab every Uniswap pair and save it to an array using the Graph API.
My problem occurs when I make my 7th request to the API.
Initially, I thought I was being rate limited because I was fetching 1000 tokens at a time, but after adding a 10 second wait between calls and decreasing the fetched tokens from 1000 to 10, it still stops on the 7th loop.
The script works perfectly until this point.
const axios = require('axios');
const fs = require('fs');
async function getTokens(skip) {
try {
const query = `
query tokens($skip: Int!) {
tokens(first: 10, skip: $skip) {
id
name
symbol
}
}
`;
const variables = {
skip: skip
};
const headers = {
"Content-Type": "application/json"
};
const { data } = await axios.post("https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3", {
query,
variables
}, {
headers
});
return data.data.tokens;
} catch (err) {
console.error(err);
return []
}
}
async function saveTokens(tokens) {
try {
await fs.promises.writeFile("uniTokens.json", JSON.stringify(tokens), { flag: "w" });
} catch (err) {
console.error(err);
}
}
async function main() {
let skip = 0;
let tokens = [];
const retrievedIds = new Set();
while (true) {
const newTokens = await getTokens(skip);
if (newTokens.length === 0) {
console.log("Reached end of tokens, finishing up...");
break;
}
// Only save tokens that haven't been retrieved before
const newIds = new Set(newTokens.map(token => token.id));
newIds.forEach(id => {
if (!retrievedIds.has(id)) {
tokens.push(newTokens.find(token => token.id === id));
retrievedIds.add(id);
}
});
console.log(`Retrieved ${tokens.length} tokens`);
await saveTokens(tokens);
skip += 1000;
// delay the next request by 10 seconds
//await new Promise(resolve => setTimeout(resolve, 10000));
}
}
main();
This is the error that it produces:
TypeError: Cannot read properties of undefined (reading 'tokens')
at getTokens (/root/unipairs/uni:31:26)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async main (/root/unipairs/uni:52:27)
Reached end of tokens, finishing up...

Axios Async Await Function returns 'undefined' results (Using while Loop)

I am trying to get data from an api using axios.
I am first getting the token, and then using the token to make the request. Since there is a limit on how much information can be responded, I have to use a while loop to get all the data and store it all to an empty array.
However, I am getting a bunch of 'undefined', I read other similar articles online with regard to this return, and most of them is because of "no return", but since I am using a while loop, where can I return the data?
const getDailySales = async (req, res) => {
try {
const res_token = await axios.post(
`https://cysms.wuuxiang.com/api/auth/accesstoken?appid=${process.env.TCSL_APPID}&accessid=${process.env.TCSL_ACCESSID}&response_type=token`
);
const token = res_token.data.access_token;
var list = [];
var pageTotal = true;
var pageNo = 1;
while (pageTotal) {
var salesData = await axios.post(
`https://cysms.wuuxiang.com/api/datatransfer/getserialdata?centerId=${process.env.TCSL_CENTERID}&settleDate=2022-09-30&pageNo=${pageNo}&pageSize=20&shopId=12345`
{},
{
headers: {
access_token: `${token}`,
accessid: `${process.env.TCSL_ACCESSID}`,
granttype: "client",
},
}
);
list.push(salesData);
console.log(salesData.data.data.billList.shop_name);
if (salesData.data.data.pageInfo.pageTotal !== pageNo) {
pageNo += 1;
} else {
pageTotal = false;
}
}
} catch (error) {
console.log(error);
}
};
Above implementation would work.
Return list just before catch and after ending of the while loop
} else {
pageTotal = false;
}
}
return list;
} catch (error) {
console.log(error);
}
};
Few suggestions
Use let/const instead of var.
Can elaborate more error handling
return list like this:
return res.status(200).json({list});

Mongoose data Writing issue in Javascript

Just started with a javascript couple of days back. I am trying to use MongoDB with mongoose to write the data but it is not writing even though the connection is established.
I would really appreciate it if you can help me point out what I am missing here.
dbtest.js - module to create connection
require("dotenv").config();
const mongoose = require("mongoose");
const Block = require("./model/blockSchema");
const connectDB = async () => {
try {
await mongoose.connect(process.env.DATABASE_URI, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
console.log("CONNECTED to MONGODB DATABASE");
} catch (err) {
console.error(err);
}
};
module.exports = connectDB;
blockchain.js
Even though I have verified the connection before calling the main method, it looks like the connection is not available to class methods.
require("dotenv").config();
const { hash256 } = require("../util/util");
const block = require("./block");
const blockchain = require("./blockHeader");
const Block = require("../database/model/blockSchema");
const { mongoose } = require("mongoose");
const connect = require("../database/dbtest");
VERSION = 1;
const ZERO_HASH = String("0").padStart(64, "0");
// Create connection
connect();
class Blockchain {
GenesisBlock() {
try {
const BlockHeight = 0;
const prevBlockHash = ZERO_HASH;
this.addBlock(BlockHeight, prevBlockHash);
} catch (err) {
console.log(`Error in Genesis Blockchain Function \n ${err}`);
}
}
addBlock(BlockHeight, prevBlockHash) {
let timestamp = Date.now();
let Transaction = `Codies Alert sent ${BlockHeight} to Anni`;
let merkleRoot = hash256(Transaction);
let bits = "ffff001f";
let blockHeader = new blockchain.BlockHeader(
VERSION,
prevBlockHash,
merkleRoot,
timestamp,
bits
);
//Mine a Block
blockHeader.mine();
// Create Schema Instance to Write the data
let BlockObj = new Block({
Height: BlockHeight,
BlockSize: 1,
blockHeader: {
version: 1,
prevBlockHash: "00000",
timestamp: timestamp,
bits: bits,
nonce: blockHeader.nonce,
blockHash: blockHeader.blockhash,
},
TxCount: 1,
Transactions: Transaction,
});
// Mongoose Schema, Write data
BlockObj.save((err) => {
if (err) return console.log(`Error while Writing the Block ${err}`);
console.log(`Block Written Successfully!!!!!!!`);
});
this.chain = new block.Block(BlockHeight, 1, blockHeader, 1, Transaction);
console.log(BlockObj);
}
// Main Function to trigger the process
main() {
this.chain = "";
this.GenesisBlock();
while (true) {
let lastBlock = this.chain;
let Blockheight = lastBlock.Height + 1;
let prevBlockHash = lastBlock.BlockHeader.blockhash;
this.addBlock(Blockheight, prevBlockHash);
}
}
}
mongoose.connection.once("open", async () => {
console.log("Connection Verified and ready to write data");
// Create an instance and call the main method
const blockchain = new Blockchain();
blockchain.main();
});
Issue was due to async/await. Here is the updated code that works.
require("dotenv").config();
const { hash256 } = require("../util/util");
const block = require("./block");
const blockchain = require("./blockHeader");
const Block = require("../database/model/blockSchema");
const connect = require("../database/dbtest");
const getLastBlock = require("../database/read");
VERSION = 1;
const ZERO_HASH = String("0").padStart(64, "0");
let mongoose = "";
class Blockchain {
async GenesisBlock() {
try {
console.log(mongoose.connection.readyState);
const BlockHeight = 0;
const prevBlockHash = ZERO_HASH;
await this.addBlock(BlockHeight, prevBlockHash);
} catch (err) {
console.log(`Error in Genesis Blockchain Function \n ${err}`);
}
}
async addBlock(BlockHeight, prevBlockHash) {
let timestamp = Date.now();
let Transaction = `Codies Alert sent ${BlockHeight} to Anni Maan`;
let merkleRoot = hash256(Transaction);
let bits = "ffff001f";
let blockHeader = new blockchain.BlockHeader(
VERSION,
prevBlockHash,
merkleRoot,
timestamp,
bits
);
blockHeader.mine();
let BlockObj = {
Height: BlockHeight,
BlockSize: 1,
blockHeader: {
version: 1,
prevBlockHash: blockHeader.prevBlockhash,
merkleroot: merkleRoot,
timestamp: timestamp,
bits: bits,
nonce: blockHeader.nonce,
blockhash: blockHeader.blockhash,
},
TxCount: 1,
Transactions: Transaction,
};
// Mongoose Schema, Write data
try {
await new Block(BlockObj).save();
console.log(BlockObj);
console.log("Block Written Successfully");
this.chain = new block.Block(BlockHeight, 1, blockHeader, 1, Transaction);
} catch (err) {
console.log(`Error in addBlock Function \n ${err}`);
}
}
// Main Function to trigger the process
async main() {
const lastBlock = await getLastBlock.main(true);
console.log(lastBlock[0]);
this.chain = lastBlock[0];
if (!this.chain) {
await this.GenesisBlock();
}
while (true) {
console.log(mongoose.connection.readyState);
let lastBlock = this.chain;
let Blockheight = lastBlock.Height + 1;
let prevBlockHash = lastBlock.blockHeader.blockhash;
await this.addBlock(Blockheight, prevBlockHash);
}
}
}
const createConnection = async () => {
try {
mongoose = await connect();
const blockchain = new Blockchain();
blockchain.main();
} catch (err) {
console.log("Error while con", err);
}
};
createConnection();
The issue in your code is due to the asynchronous programming, whenever you make a db call it is an asynchronous request and you will need to use async-await or Promises to make it work. In your previous code you haven't used async await thats why your data is not getting written into the db.
You can learn about async await here link and about promises here.
Please go through it, promises are the core concept of js and you will definitely need it if you are using node js.
Also try to learn about synchronous and asynchronous from here, these are really necessary and base of node js.
All these db calls needs to call with promises or async await to make it work.

Is this Transaction valid?

I am wondering if this transaction is even valid and actually ensuring quantity is being the most up to date.
async function deductQuantity(orders: [Order]) : Promise<boolean> {
try {
let document = await admin.firestore().collection("MenuItems")
orders.forEach(async (order)=> {
let itemDoc = (await document.where(`item.catalogType`, "==", order.catalogType).where(`item.id`, "==", order.item.id))
let get = await itemDoc.get()
get.forEach(async a=> {
const pp = document.doc(a.id)
await admin.firestore().runTransaction(async (t)=> {
const mostRecentDoc = await t.get(pp)
const data = await mostRecentDoc.data()
if (data == undefined){
return
}
const newQuantity = data.item.quantity - order.quantity
await t.update(pp, {[`item.quantity`] : newQuantity})
})
})
})
return true
} catch (error) {
console.log("dum: " + error)
return false
}
}
the part where I do let get = await itemDoc.get(), and get.ForEach, is kind of unnecessary because I know, that it will only return one document that matches the query field, but I need to forEach it in order to get the child component\s. Anyways, is it a valid transaction?

Return response after async await function in nodejs

I am develop a small app, I have a problem. After I await to run query from Mongodb then I use map method to do some task then I return my response, but the response always return promise and I have not necessary data. Here my code
exports.getListChallenges = async function (req, res) {
var status_code = Consts.STATUS_CODE.ERROR.UNKOWN_ERROR;
try {
var page_number = parseInt(req.params.page_number);
var page_size = parseInt(req.params.page_size);
var challenges = await Challenge.find()
.skip(page_size * (page_number - 1))
.limit(page_size);
status_code = Consts.STATUS_CODE.SUCCESS.DATA_FOUND;
challenges = challenges.map(async function (o) {
var challenger_club = await Club.findById(o.challenger_club_id);
var challege = {
challenge: o,
challenger_club_name: challenger_club.club_name,
challenger_club_avatar: challenger_club.avatar
};
return challege;
});
res.json({
"status": true,
"message": "Challenges found !",
"status_code": status_code,
"challenges": challenges
});
} catch (error) {
status_code = Consts.STATUS_CODE.ERROR.UNKOWN_ERROR;
res.json({
"status": false,
"status_code": status_code,
"error": error
})
}
}
the "challenges" in response always empty , how can I solve it ? thank !
You're very close - you've mapped each item in the initial challenges to a Promise that resolves to the challenge object you want, now you need to call Promise.all on that array of Promises to get the values they resolve to. To keep the code clear, it would be best not to reassign challenges unless it's necessary - call the new array of Promises something else, like challengePromises:
exports.getListChallenges = async function(req, res) {
var status_code = Consts.STATUS_CODE.ERROR.UNKOWN_ERROR;
try {
var page_number = parseInt(req.params.page_number);
var page_size = parseInt(req.params.page_size);
var challenges = await Challenge.find()
.skip(page_size * (page_number - 1))
.limit(page_size);
status_code = Consts.STATUS_CODE.SUCCESS.DATA_FOUND;
var challengePromises = challenges.map(async function(o) {
var challenger_club = await Club.findById(o.challenger_club_id);
var challege = {
challenge: o,
challenger_club_name: challenger_club.club_name,
challenger_club_avatar: challenger_club.avatar
};
return challege;
});
var challengeObjs = await Promise.all(challengePromises);
res.json({
"status": true,
"message": "Challenges found !",
"status_code": status_code,
"challenges": challengeObjs
});
} catch (error) {
status_code = Consts.STATUS_CODE.ERROR.UNKOWN_ERROR;
res.json({
"status": false,
"status_code": status_code,
"error": error
})
}
}
Also, just a suggestion, but if you're using async and await, you might consider going all the way with ES6+ syntax, such as const instead of var, arrow functions when possible, shorthand object properties, among other things - they can make code more concise and readable. For example, I'd refactor your code to:
exports.getListChallenges = async function(req, res) {
try {
const page_number = parseInt(req.params.page_number);
const page_size = parseInt(req.params.page_size);
const challengeData = await Challenge.find()
.skip(page_size * (page_number - 1))
.limit(page_size);
const status_code = Consts.STATUS_CODE.SUCCESS.DATA_FOUND;
const challengePromises = challengeData.map(async (o) => {
const {
club_name: challenger_club_name,
avatar: challenger_club_avatar
} = await Club.findById(o.challenger_club_id);
return {
challenge: o,
challenger_club_name,
challenger_club_avatar
};
});
const challenges = await Promise.all(challengePromises);
res.json({
status: true,
message: "Challenges found !",
status_code,
challenges
});
} catch (error) {
const status_code = Consts.STATUS_CODE.ERROR.UNKOWN_ERROR;
res.json({ status: false, status_code, error })
}
}
Note that the status_code will always be Consts.STATUS_CODE.ERROR.UNKOWN_ERROR when there's an error, given the logic on your original code - there's no need to reassign it.

Categories

Resources