Get users info from nextcloud api - javascript

Using the node-nextcloud module, how to get emails for all users from an array? For example, the nc.users.get() method works with one userID, but what about when it's a hundred userIDs? I tried to work with the method directly and through a loop, but the result is not satisfactory.
const fs = require('fs')
const NextCloud = require('node-nextcloud');
var nc = NextCloud("Domain", "User", "Password");
var arr = []
//get users id into array
nc.users.list(null, (err, ocs)=> {
if (err) {
console.error(err);
}
arr = ocs.data.users
console.log(arr)
})
//try to get email for each id from array
nc.users.get('' , (err, ocs)=>{
if (err) {
console.log(err);
}
arr.forEach(email => {
console.log(ocs.data.email)
})
})

I'm not familiar with next cloud but it would appear to me you want to use the list method first to get all the user ids, then loop through the result from that list to get each user. Depending on the number of users it could be quite a lengthly operation. You're currently calling two asynchronous methods but the second one doesn't wait for the first one to finish. As I say I'm not familiar with next cloud but the below is a rough stab at what you might need
async function getUsers() {
let users = await nc.users.list();
const userEmails = await Promise.all(users.map(async (userId) => {
const user = await nc.users.get(userId);
return user.email;
}))
console.log("user emails", userEmails);
}
getUsers();

I managed to achieve the result in the following way:
nc.users.list(null, (err, ocs)=> {
if (err) {
console.error(err);
}
arr = ocs.data.users
//console.log(arr)
arr.forEach((item, index, array) => {
nc.users.get(`${item}`, (err, ocs) => {
if (err) {
console.error(err);
}
console.log(ocs.data.email);
})
})
})

Related

JavaScript - Async/Await - How to Return result from nested functions?

I'm new to Node.js and having difficulty working with async/await model. The issue I'm having is with nested function calls returning data in an async/await method, I always get 'undefined'. My functions structure is like this:
const findCustomerOrder = async (id) => {
const orders = await Order.find({custoemrId:id})
.then(async (order) => {
if(order)
{
const products = await findProductsByOrderId(order._id)
.then(async (result) => {
for(const r in result){
console.log("Product ID: ", result._id);
}
});
}
else
console.log("order not found");
});
}
const findProductsByOrderId = async (id) => {
try{
const products = await Products.find({orderId:id}, (error, data) => {
if(error) console.log(error);
else
return data;
});
}
catch(err){
return 'error!!';
}
}
I understand that if the top-level call is async/await then all the nested calls should be awaited as well that I've tried to do.
What am I doing wrong here?
Get rid of all the then, and also of the callback in the DB query. Use only await. It will make the whole code a lot easier to reason about and it should also solve your issue as part of the restructuring.
In the example below I also added a loop over the orders, because you are fetching all orders, as array, but then your code was behaving as if it got only one. I also fixed the products loop.
However, the way you fetch products doesn't seem to be right anyway, see my comment below. I didn't fix it because I don't know how your database structure looks, so I don't know what the right way would be.
async function findCustomerOrder (id) {
const orders = await Order.find({ customerId: id })
for (const order of orders) {
console.log(`Order ID: ${order._id}`)
const products = await findProductsByOrderId(order._id)
for (const product of products) {
console.log(`Product ID: ${product._id}`);
}
}
}
async function findProductsByOrderId (id) {
// Note: I don't think this is right. It seems to find all
// products with the given ID - which will always be one -
// and that ID would be the _product_ ID and not the order ID.
return await Products.find({ _id: id })
}
Preemptive comment reply: The return await is intentional, and this is why.
You should remove the then and also the callback and you can do something like below
const findCustomerOrder = async(id) => {
try {
const orders = await Order.find({
custoemrId: id
});
if (orders) {
let promiseArray = [];
orders.forEach(order => {
promiseArray.push(findProductsByOrderId(order._id));
});
let results = await Promise.all(promiseArray);
return results;
}
return "order not found";
} catch (err) {
throw err;
}
}
const findProductsByOrderId = async(id) => {
try {
const products = await Products.find({
_id: id
});
return products;
} catch (err) {
throw err;
}
}

JavaScript, Node.JS : unable to update a variable in a loop

I am building an API via Node.js.
I have a loop that add values to an initially empty array, but the array stays empty. The following is my code.
get_doc_name = async (event) => {
const connection = await db.connection()
try {
const doc_ids = event.doc_ids
// array that has JSONs as element
// e.g. [ {1 : 'certificate.pdf'}, {2: 'report.jpeg'}]
let dict = []
await doc_ids.map(async function(id) {
const sql = "SELECT link FROM document WHERE id=?"
await connection.query(sql, id, async function (err, result) {
if (err) throw err
const link = await result[0].link
const doc_name = await link.split('/').pop()
await dict.push({
key: id,
value: doc_name
})
console.log(dict)
})
})
return dict
} catch (err) {
console.log(err.message)
} finally {
connection.end()
console.log('MySQL connection closed')
}
}
The function returns an empty array. However, it prints the updated array when console.log(dict) is executed. I need the function to return the updated array.
Moreover, when the function is called, the codes in the 'finally' statement are executed before the codes in 'try' statement. The program prints MySQL connection closed before it prints dict.
Any help is highly appreciated!
As you are retrieving data from DB, it is better to use serial Promise instead of parallel Promises to get data from DB. The way I do is in serial Promise.
It is a better practice to get data with SELECT link FROM document WHERE id in () instead of id= in your SQL because you don't need to make multiple I/O to the DB in your case.
However, I didn't change this part of logic.
Also, you should promisefy your connection.query and return a value from it.
I make it in a getResult() function.
get_doc_name = async event => {
try {
const connection = await db.connection();
try {
const doc_ids = event.doc_ids;
// array that has JSONs as element
// e.g. [ {1 : 'certificate.pdf'}, {2: 'report.jpeg'}]
let dict = [];
//Promises in serial here
for (const id of doc_ids) {
const result = await getResult(connection, id);
dict.push(result);
}
console.log(dict);
return dict;
} catch (err) {
console.log(err);
} finally {
await connection.end();
console.log("MySQL connection closed");
}
} catch (err) {
console.log(err);
}
};
function getResult(connection, id) {
return new Promise((resolve, reject) => {
const sql = "SELECT link FROM document WHERE id=?";
connection.query(sql, id, function (err, result) {
if (err) reject(err);
const link = result[0].link;
const doc_name = link.split("/").pop();
resolve({ key: id, value: doc_name });
});
});
}
get_doc_name()
.then(dict => console.log(dict))
.catch(console.log);

(node/express) Collecting multiple pieces of data from database queries and sending to index.ejs?

I'm obtaining multiple pieces of info from a postgres database and using them to render a web page via express in node.
I can make a single query of the "measurements" database and pass it in no problem eg:
pool.query("SELECT * from measurements", (err, result) => {
if(err) {
throw err;
} else {
var set1= JSON.stringify(result);
app.get("/", (req, res) => { res.render("index", { set1: set1}); });
}
});
My issue is making multiple queries and rendering them all together. At first I tried to store the query results in separate variables outside of a function and passing all of them like so:
var set1= pool.query("SELECT * from measurements");
var set2= pool.query("DIFFERENT QUERY* from measurements");
app.get("/", (req, res) => { res.render("index", { set1: set1, set2: set2 }); });
but that didn't work because of promises/synchronous method (I think) because it just tells me it's awaiting a promise (or something like that, I can't quite remember). I tried to use some await stuff as a fix but nothing worked. (or I wasn't implementing correctly)
So what's the best way to do this? I thought about either:
build a function that makes all the necessary queries, declares the variables, and then renders the page at the end using all of them (I think this is where await/async works?)
somehow combining all the query results into a single object and just sending that single object with all the data, then sorting/classifying/displaying it on the client side. (or just sending the whole database to the client, but that seems like a bad idea)
but I'm not exactly sure the best way to do either of these. Any help would be appreciated, am new to JS
Well doing it with async / await is pretty straight forward:
app.get("/", async (req, res) => {
var set1 = await getMeasurments("SELECT * FORM BLABLA");
var set2 = await getMeasurments("SELECT * FORM BLABLA");
res.render("index", { set1, set2 });
});
function getMeasurments(query) {
return new Promise((res, rej) => {
pool.query(query, (err, result) => {
if (err) {
rej(err);
} else {
res(result);
}
});
});
}
Just make a new function and promisfy it
const query = async(query) => {
const client = await pool.connect()
const result = await client.query(query)
client.release()
return result;
})
const requests = [
query("SELECT * from measurements"),
query("DIFFERENT QUERY* from measurements")
]
Promise.All(requests).then(results => {
return results.reduce((acc, curr) => {
return Object.assign(acc, curr)
}, {})
}).then(data => {
res.render("index", data)
})
Taking your initial code, you can, first (1), promisify the pool.query and then (2) call it with await inside an async function; and (3) use the composed function in you get route
const poolquery = util.promisify(pool.query); // (1)
async function renderindex(req,res) { // (2)
try {
let set1 = await poolquery ("SELECT * FROM SET1 ...");
let set2 = await poolquery ("SELECT * FROM SET2 ...");
res.render("index", { set1, set2 });
} catch (err) {
res.status(500).send(err);
}
}
app.get("/", renderindex); // (3)

node.js waiting for the db query results for the next step

I have a api code like below is written via node.js. Node.js is asynchronous so next step executing before db query result. In that time system had not got the data so gives an error. I have searched callback structure, promise and await but could not solve it. Thanks.
var dateTime = require("node-datetime");
var dt = dateTime.create();
module.exports = {
myOrderImportFromFileAPI: (req, res) => {
//let age = req.params.Age;
// send the player's details to the database
let userName = req.body.userName;
let companyID = req.body.companyId;
let channelId = req.body.channelId;
let orders = req.body.orders;
//default values
let orderTypeId = 1;
let orderStatusId = 1;
let createdDate = dt.format("Y-m-d H:M:S");
let getDate = dt.format("Y-m-d H:M:S");
db.query(`select ID,Code from operationcenters where CompanyID=${companyID}`, (err, result) => {
if (err) {
return res.status(500).send(err);
}
var operationCenterId = result[0].ID
result.find((e) => console.log(e.ID));
});
console.log("operationCenterId");
console.log(operationCenterId);
console.log("channelId");
console.log(channelId);
console.log(orders);
let query = "select ID value, Name label from channels"
db.query(query, (err, result) => {
if (err) {
return res.status(500).send(err);
}
res.send(`1`);
});
},
};
const dateTime = require("node-datetime");
const dt = dateTime.create();
const myOrderImportFromFileAPI = async (req, res) => {
const { userName, companyID, channelId, orders } = req.body
const orderTypeId = 1;
const orderStatusId = 1;
const createdDate = dt.format("Y-m-d H:M:S");
const getDate = dt.format("Y-m-d H:M:S");
try {
const queryData = await queryDatabase()
res.send(queryData).status(200)
} catch(error) {
res.send(error).status(500)
}
}
function queryDatabase() {
const query = "select ID value, Name label from channels"
return new Promise((resolve, reject) => {
db.query(query, (err, result) => {
if (err) {
reject(err)
}
resolve(result)
});
})
}
soomething like above will get you on the way. I haven't tested this so I can't say it works 100% but it is a good starting point. The reason for this is because by default the mysql db doesn't support asynchronous the modern way. That's why many people wrap in Bluebird(?) which is a library for creating promises which is a frequent dependency on drivers and such. So what you are doing is creating an async function with const myOrderImportFromFileAPI = async (req, res) => {}
After that then creating your variables, since you aren't redeclaring them then use const the first line after the function declaration is destructuring the object. Since we know the name of data and naming it as such then you can save lines with that and makes it more readible.
Finally, we wrap the async call in a try catch statement since we are calling a function which returns a promise(we don't need to call name async function queryDatabase since we are not performing anything asynchronous inside)
inside the actual function queryDatabase we will return with a resolved value of result or return a rejected value of err which will be caught in our try catch statment and will send the appropriate response..

Async reduce returning promises

I have an array of objects and I have to add one property on each of the objects coming from and async function
I am doing an Array.reduce to iterate on each of the elements and return just one result: One array of objects with the new property.
I have this
const res = await resultOne.reduce(async (users = [], user, i) => {
let userName;
try {
let { name } = await names.getNames(user.id);
userName = name;
} catch (error) {
throw error;
}
delete user.id;
users.push({ ...user, userName });
return users;
}, []);
But I get the message
Push is not a function of users
And this is because I think is a promise.
How can I handle async requests in a reduce or a map
Yes, users is a promise. Don't use reduce with async functions. You could do something like await users in the first line, but that would be pretty inefficient and unidiomatic.
Use a plain loop:
const users = [];
for (const user of resultOne) {
const { name } = await names.getNames(user.id);
delete user.id;
users.push({ ...user, userName: user });
}
or, in your case where you can do everything concurrently and create an array anyway, the map function together with Promise.all:
const users = await Promise.all(resultOne.map(async user => {
const { name } = await names.getNames(user.id);
delete user.id;
return { ...user, userName: user };
}));
Because it's an async function, every time you return something it gets wrapped in a promise. To fix this you need to set the starting array as a promise and then await for the accumulator on each iteration.
const res = await resultOne.reduce(async (users, user, i) => {
try {
return [
...await users,
{ ...user, userName: await names.getNames(user.id.name) }
]
} catch (error) {
console.log(error)
}
}, Promise.resolve([]));

Categories