Azure IoT Edge node SDK invokeDeviceMethod not working asynchronously - javascript

I am trying to return a result for a direct method call in an async fashion.
I tried:
var client = Client.fromConnectionString(process.env.AZ_IOT_CONNECTION_STRING);
var methodParams = {
methodName: "method",
payload: 10, // Number of seconds.
responseTimeoutInSeconds: 60
};
// Call the direct method on your device using the defined parameters.
client.invokeDeviceMethod(
req.params.deviceId,
methodParams,
(err, result) => {
if (err) {
console.error(err);
} else {
console.log("success");
}
}
);
On Device:
const method = async (request, response) => {
const longProcess = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 5000);
});
};
try {
await longProcess();
response.send(200, `success`);
} catch (error) {
response.send(500, `Error: ${error}:`);
}
};
client.onDeviceMethod("method", method);
Expected: Returns success after 5 seconds
Actual: returns "BadRequest" "errorCode :400027" but the method is executed correctly.

please work with the promise function instead of the callback like so:
async deviceSyncInvoke(deviceID: string, methodName: string, data:any, responseTimeoutInSeconds?: number): Promise<[number, any]> {
try{
if (!this.client){
await this.openClient();
}
const methodParam: DeviceMethodParams = {
methodName: methodName,
payload: JSON.stringify(data)
}
// The minimum and maximum values for responseTimeoutInSeconds are 5 and 300 seconds.
//If timeout is not provided, it the default value of 30 seconds is used
if (responseTimeoutInSeconds && responseTimeoutInSeconds !== undefined) {
methodParam.responseTimeoutInSeconds = responseTimeoutInSeconds;
}
const res: ResultWithIncomingMessage<any> = await this.client.invokeDeviceMethod(deviceID, methodParam);
if (res.message.statusCode == undefined || res.message.statusCode > 299) {
throw new Error(`statusCode: ` + res.message.statusCode + `statusMessage: ` + res.message.statusMessage);
}
return [2001, "message invoking command with res: " + res];
}catch(err) {
return [5000, "error deviceSyncInvoke: " + err];
}
}

Related

Exponential Backoff not returning data in promise

I am not sure if I am approaching this the correct way. I have tried a few different versions of the implementation but am putting the one here that works when the backoff path is NOT used.
So, I have an index.js that is just:
import { Lizard } from './lizard.js';
const lizard = new Lizard();
const global_data = await lizard.global();
console.log(global_data);
In my lizard.js I have a class with functions but for the sake of saving space and noise I will only place the ones that matter here:
export class Lizard {
global() {
const path = '/global';
return this._request(path);
};
_buildRequestOptions(path, params) {
if (isObject(params)) params = querystring.stringify(params);
else params = undefined;
if (params == undefined) path = `/api/v${API_VERSION}${path}`;
else path = `/api/v${API_VERSION}${path}?${params}`;
// Return options
return {
path,
method: 'GET',
host: HOST,
port: 443,
timeout: Lizard.TIMEOUT,
};
};
async _request(path, params) {
const options = this._buildRequestOptions(path, params);
const maxRetries = 10;
function waitFor(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
async function request(options, retries) {
if (retries > 0) {
const timeToWait = 15000 * retries;
console.log(`waiting for ${timeToWait}ms...`);
await waitFor(timeToWait);
}
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let body = [];
res.on('data', (chunk) => {
body.push(chunk);
});
res.on('end', () => {
try {
body = Buffer.concat(body);
body = body.toString();
if (body.startsWith('<!DOCTYPE html>')) {
_WARN_('Invalid request', 'There was a problem with your request. The parameter(s) you gave are missing or incorrect.');
} else if (body.startsWith('Throttled')) {
_WARN_('Throttled request', 'There was a problem with request limit.');
}
body = JSON.parse(body);
} catch (error) {
reject(error);
};
const returnObject = ReturnObject(
!(res.statusCode < 200 || res.statusCode >= 300),
res.statusMessage,
res.statusCode,
body
)
if (returnObject.code != 429) {
resolve(returnObject);
} else {
if (retries < maxRetries) {
console.log('retrying...');
return request(options, retries + 1);
} else {
console.log("Max retries reached. Bubbling the error up");
resolve(returnObject);
}
}
});
});
req.on('error', (error) => reject(error));
req.on('timeout', () => {
req.abort();
reject(new Error(`Lizard API request timed out. Current timeout is: ${Lizard.TIMEOUT} milliseconds`));
});
// End request
req.end();
});
}
return await request(options, 0);
};
}
I was trying to do this in a very difficult way. For anyone else that may stumble upon this here was my ultimate solution:
lizard.js:
async function request(path, params, retries = 0, maxRetries = 10) {
let options = await buildRequestOptions(path, params);
return new Promise((resolve, reject) => {
let req = https.request(options, (res) => {
let body = [];
// Set body on data
res.on('data', (chunk) => {
body.push(chunk);
});
// On end, end the Promise
res.on('end', async () => {
try {
body = Buffer.concat(body);
body = body.toString();
// Check if page is returned instead of JSON
if (body.startsWith('<!DOCTYPE html>')) {
_WARN_('Invalid request', 'There was a problem with your request. The parameter(s) you gave are missing or incorrect.');
} else if (body.startsWith('Throttled')) {
_WARN_('Throttled request', 'There was a problem with request limit.');
}
// Attempt to parse
body = JSON.parse(body);
} catch (error) {
reject(error);
};
if (res.statusCode == 429 && retries < maxRetries) {
const timeToWait = 60000 + (1000 * retries);
console.error('Throttled request ' + retries + ' time(s)');
console.log(`Retrying in ${timeToWait}ms...`);
setTimeout(() => {
resolve(request(path, params, retries + 1));
}, timeToWait);
} else {
resolve(
objectify(
!(res.statusCode < 200 || res.statusCode >= 300),
res.statusMessage,
res.statusCode,
body
)
);
}
});
});
// On error, reject the Promise
req.on('error', (error) => reject(error));
// On timeout, reject the Promise
req.on('timeout', () => {
req.abort();
reject(new Error(`Lizard API request timed out. Current timeout is: ${TIMEOUT} milliseconds`));
});
// End request
req.end();
});
};
I still resolve the object on fail as 429 is too many requests, so anything else needs to bubble up. On top of that if max retries is met, then if I see a 429 I know that I exceeded.

How to use await to get JSON response and retry if it's returned NULL

My app has a search function that returns JSON.
The JSON will return NULL if it's not -quite- ready and needs a few more seconds to process.
If the JSON request is returned as NULL, I need to retry every second until it's not NULL.
Here is my JS:
async search(q, callback) {
const response = await get(this.urlValue, {
query: { q: q },
responseKind: 'json'
})
if (response.ok) {
const list = await checkIfResponseNotNull();
}
function checkIfResponseNotNull() {
return new Promise(resolve => {
setTimeout(() => {
if (response.json === null) {
// Try again?
} else {
return callback(response.json)
}
}, 500)
})
}
}
You could recursively call the search method when response, or response.json, is null.
const search = async (q, callback) => {
console.log("searching for: " + q);
try {
const response = await get();
console.log(response);
if (response?.json) {
console.log("response found");
return callback(response.json);
}
console.log("retry search");
await search(q, callback);
} catch {
// await search(q, callback) // Also try again when request fauls?
}
};
A simple example can be found here: here

Function return null, await result not applied

I am using the below function within post method. The async-await is used but in transferAmount totalBalance is not updated when I call the function inside the post route. The return from the function is not proper. I need guidance so that it returns the object with updated values.
async function transferAmount(fromAccountId, toAccountId, amount) {
const session = await mongoose.startSession();
const options= {session, new:true}
let sourceAccount, destinationAccount;
const BASICSAVNGS_MAX_BALANCE = 1500;
const result = {
newSrcBalance: 0,
totalDestBalance:0,
transferedAt:moment.now()
}
try {
session.startTransaction();
const source= await Account.findByIdAndUpdate(
{_id:sourceAccount._id},
{$inc:{balance:-amount}},
options
);
if(source.balance <0) {
// Source account should have the required amount for the transaction to succeed
const errorMessage='Insufficient Balance with Sender:';
throw new ErrorHandler(404,errorMessage);
}
const destination = await Account.findByIdAndUpdate(
{_id:destinationAccount._id},
{$inc:{balance:amount}},
options
);
// The balance in ‘BasicSavings’ account type should never exceed Rs. 50,000
if((destination.accountType.name === 'BasicSavings') && (destination.balance > BASICSAVNGS_MAX_BALANCE)) {
const errorMessage=`Recepient's maximum account limit reached`;
throw new ErrorHandler(404,errorMessage);
}
await session.commitTransaction();
result.transferedAt= moment.now() //*UPDATE THE TRANSFER TIMESTAMP
result.newSrcBalance = source.balance; //*UPDATE THE SOURCE BALANCE
session.endSession();
// finding total balance in destination account
await User.findById(destination.user.id, async function(err,user) {
if(err) {
const errorMessage=`Recepient not found!`;
console.log(err);
throw new ErrorHandler(404,errorMessage);
} else {
if(user.accounts) {
await Account.find({
'_id' :{$in:user.accounts}
}, function(err,userAccounts) {
totalDestBalance = userAccounts.reduce( (accumulator,obj) => accumulator+obj.balance,0);
result.totalDestBalance = totalDestBalance; //*UPDATE THE TOTAL BALANCE
console.log(result);
return result;
});
}
}
});
}
catch (error) {
// Abort transaction and undo any changes
await session.abortTransaction();
session.endSession();
throw new ErrorHandler(404,error);
} finally {
if(session) {
session.endSession();
}
}
}
module.exports = transferAmount;
Result of above function is
{
newSrcBalance: 940,
totalDestBalance: 1060,
transferedAt: 1594982541900
}
But inside the post request below it is {}
const result = await transferAmount(fromAccountId, toAccountId, amount);
You are not returning something inside the function.
User.findById - this receives a callback for returning something.
You can convert it as async/await syntax or have to resolve the result with promise.
Like below:
try {
const user = await User.findById(destination.user.id);
if (user.accounts) {
const userAccounts = await Account.find({ _id: { $in: user.accounts } });
totalDestBalance = userAccounts.reduce((accumulator, obj) => accumulator + obj.balance, 0);
result.totalDestBalance = totalDestBalance; //*UPDATE THE TOTAL BALANCE
console.log(result);
return result;
}
} catch (err) {
const errorMessage = `Recepient not found!`;
console.log(err);
throw new ErrorHandler(404, errorMessage);
}
Or:
return new Promise((resolve, reject) => {
User.findById(destination.user.id, async function(err, user) {
if (err) {
const errorMessage = `Recepient not found!`;
console.log(err);
reject(err);
} else {
if (user.accounts) {
await Account.find(
{
_id: { $in: user.accounts },
},
function(err, userAccounts) {
totalDestBalance = userAccounts.reduce((accumulator, obj) => accumulator + obj.balance, 0);
result.totalDestBalance = totalDestBalance; //*UPDATE THE TOTAL BALANCE
console.log(result);
resolve(result);
}
);
}
}
});
});
I may be wrong but i cannot see a return statement in you transferAmount function.

How to achieve recursive Promise calls in Node.js

I am calling an API where I can only fetch 1000 records per request,
I was able to achieve this using recursion.
I am now trying to achieve the same using promises, I am fairly new to Node.js and JavaScript too.
I tried adding the recursion code in an if else block but failed
var requestP = require('request-promise');
const option = {
url: 'rest/api/2/search',
json: true,
qs: {
//jql: "project in (FLAGPS)",
}
}
const callback = (body) => {
// some code
.
.
.//saving records to file
.
//some code
if (totlExtractedRecords < total) {
requestP(option, callback).auth('api-reader', token, true)
.then(callback)
.catch((err) => {
console.log('Error Observed ' + err)
})
}
}
requestP(option).auth('api-reader', token, true)
.then(callback)
.catch((err) => {
console.log('Error Observed ' + err)
})
I want to execute the method using promise and in a synchronous way,
i.e. I want to wait until the records are all exported to a file and continue with my code
I think its better to create your own promise and simply resolve it when your done with your recursion. Here's a simply example just for you to understand the approach
async function myRecursiveLogic(resolveMethod, ctr = 0) {
// This is where you do the logic
await new Promise((res) => setTimeout(res, 1000)); // wait - just for example
ctr++;
console.log('counter:', ctr);
if (ctr === 5) {
resolveMethod(); // Work done, resolve the promise
} else {
await myRecursiveLogic(resolveMethod, ctr); // recursion - continue work
}
}
// Run the method with a single promise
new Promise((res) => myRecursiveLogic(res)).then(r => console.log('done'));
Here's a clean and nice solution using the latest NodeJS features.
The recursive function will continue executing until a specific condition is met (in this example asynchronously getting some data).
const sleep = require('util').promisify(setTimeout)
const recursive = async () => {
await sleep(1000)
const data = await getDataViaPromise() // you can replace this with request-promise
if (!data) {
return recursive() // call the function again
}
return data // job done, return the data
}
The recursive function can be used as follows:
const main = async () => {
const data = await recursive()
// do something here with the data
}
Using your code, I'd refactored it as shown below. I hope it helps.
const requestP = require('request-promise');
const option = {
url: 'rest/api/2/search',
json: true,
qs: {
//jql: "project in (FLAGPS)",
}
};
/*
NOTE: Add async to the function so you can udse await inside the function
*/
const callback = async (body) => {
// some code
//saving records to file
//some code
try {
const result = await requestP(option, callback).auth('api-reader', token, true);
if (totlExtractedRecords < total) {
return callback(result);
}
return result;
} catch (error) {
console.log('Error Observed ' + err);
return error;
}
}
Created this code using feed back from Amir Popovich
const rp = require('Request-Promise')
const fs = require('fs')
const pageSize = 200
const options = {
url: 'https://jira.xyz.com/rest/api/2/search',
json: true,
qs: {
jql: "project in (PROJECT_XYZ)",
maxResults: pageSize,
startAt: 0,
fields: '*all'
},
auth: {
user: 'api-reader',
pass: '<token>',
sendImmediately: true
}
}
const updateCSV = (elment) => {
//fs.writeFileSync('issuedata.json', JSON.stringify(elment.body, undefined, 4))
}
async function getPageinatedData(resolve, reject, ctr = 0) {
var total = 0
await rp(options).then((body) => {
let a = body.issues
console.log(a)
a.forEach(element => {
console.log(element)
//updateCSV(element)
});
total = body.total
}).catch((error) => {
reject(error)
return
})
ctr = ctr + pageSize
options.qs.startAt = ctr
if (ctr >= total) {
resolve();
} else {
await getPageinatedData(resolve, reject, ctr);
}
}
new Promise((resolve, reject) => getPageinatedData(resolve, reject))
.then(() => console.log('DONE'))
.catch((error) => console.log('Error observed - ' + error.name + '\n' + 'Error Code - ' + error.statusCode));

How to get a Firestore transaction to return a promise?

I asked a question two days ago with a reply "Your method must return a Promise (or an Observable)."
I have altered my code to be exactly the same as the example at "https://firebase.google.com/docs/firestore/manage-data/transactions" but the problem is it passes the result as a console log but I need to wait for the write to complete at I need the result.
orderIndex() {
let db = this.firebase.firestore();
var sfDocRef = db.collection("cities").doc("SF");
db.runTransaction(function (transaction) {
return transaction.get(sfDocRef).then(function (sfDoc) {
if (!sfDoc.exists) {
throw "Document does not exist!";
}
var newPopulation = sfDoc.data().population + 1;
if (newPopulation <= 1000000) {
transaction.update(sfDocRef, { population: newPopulation });
return newPopulation;
} else {
return Promise.reject("Sorry! Population is too big.");
}
});
}).then(function (newPopulation) {
console.log("Population increased to ", newPopulation);
}).catch(function (err) {
// This will be an "population is too big" error.
console.error(err);
});
}
I have spent a further two days trying to get a promise returned.
I have seen so many questions asking for help and receiving code suggestions in reply. Please help because I am new to this and have spent over four days on this problem.
By the way the code from firebase.google has an error in
return Promise.reject("Sorry! Population is too big.");
Error: "[ts] Property 'reject' does not exist on type '(resolver: (resolve: (val: IWhenable) => void, reject: (reason: any) => void, notify: (prog...'."
My previous question was at "How do I alter the promises in my function to stop it returning before the data arrives?"
Your function is not returning the promise and also in the then case you are not returning any value.
Try this:
orderIndex() {
let db = this.firebase.firestore();
var sfDocRef = db.collection("cities").doc("SF");
return db.runTransaction(function (transaction) { //Return here
return transaction.get(sfDocRef).then(function (sfDoc) {
if (!sfDoc.exists) {
throw "Document does not exist!";
}
var newPopulation = sfDoc.data().population + 1;
if (newPopulation <= 1000000) {
transaction.update(sfDocRef, { population: newPopulation });
return newPopulation;
} else {
return Promise.reject("Sorry! Population is too big.");
}
});
}).then(function (newPopulation) {
console.log("Population increased to ", newPopulation);
return newPopulation; //Return the value
}).catch(function (err) {
// This will be an "population is too big" error.
console.error(err);
});
}
The following code updates the index, stores it back in firestore and returns the new number.
createOrderNo() {
const getDbIndex = new Promise(
(resolve, reject) => {
if (!this.orderLive) {
this.orderLive = true;
const sfDocRef = this.db.collection('eOrderIndex').doc('orderIndex');
sfDocRef.get().
then(function (sfDoc) {
if (!sfDoc.exists) {
throw "Document does not exist!";
}
console.log('sfDoc.data()', sfDoc.data()['index'])
let index = sfDoc.data()['index'] + 1;
sfDocRef.update({ index: index });
resolve(index);
})
} else {
const reason = new Error('Already live');
reject(reason);
}
})
async function show(index) {
return new Promise(
(resolve, reject) => {
var message = 'New index ' + index;
resolve(message);
}
);
};
// call the promise
async function runPromise() {
try {
console.log('Before get');
let index = await getDbIndex;
let message = await show(index);
console.log(message);
console.log('After get');
}
catch (error) {
console.log(error.message);
}
}
(async () => {
await runPromise();
})();
}
Many thanks to Jecelyn Yeen at scotch.io

Categories

Resources