Feathersjs validation method using ldapjs module problem - node.js

I want to write a function that adds the params of an ldap server after checking if the binding with server is right otherwise the data won't get added in the db ; i have to use feathers js. I tried to write a couple of lines to validate the params before adding them to the db :
const ldap=require("ldapjs"); but the problem is that when i do console.log(errors) inside and outside of ldap.bind function : i noticed that the result is only visible inside the function but not to the rest of the code ! I need it to be visible to the other parts as well so that I can use it , I want to know how to fix that.
this is the code and what the console.log has as a result.
module.exports = (ldapConnexion, mode = "create") => {
const errors = {
url_path: [],
browsingAccount: [],
password: [],
userSearchBase:[],
};
const client=ldap.createClient({url:ldapConnexion.url_path})
client.bind(ldapConnexion.browsingAccount,ldapConnexion.password,function(err){
if (err){errors.browsingAccount.push("unvalid credentials:"+err);console.log(err)}})
console.log(errors)
const hasErrors = Object.keys(errors).reduce(
(res, errorType) =>
res || (errors[errorType] && errors[errorType].length > 0),
false
);
return { errors, hasErrors };
}

to do that we can implement the code this way : if this can interrest someone
in a different file do :
const ldap = require('ldapjs');
module.exports = (ldapConnexion, mode = "create",cb) => {
const errors = {
url_path: [],
browsingAccount: [],
password: [],
userSearchBase: [],
};
var opts ={
rejectUnauthorized: false, // To force the client not to check self-signed certificates
}
//create the ldap client with the list of informations mentionned in the form
var client = ldap.createClient({
url:ldapConnexion.url_path, tlsOptions:opts
});
client.bind(ldapConnexion.browsingAccount, ldapConnexion.password, function (err) {
if (err) {
errors.browsingAccount.push("unvalid credentials:" + err.message);
//console.log(errors);
cb(errors,true);
client.unbind(function(err){if(err){console.log(err)}});
} client.unbind(function(err){if(err){console.log(err)}});
})
// console.log(errors)
if (!ldapConnexion.url_path) {
errors.url_path.push("Url obligatoire");
}
if (!ldapConnexion.browsingAccount) {
errors.browsingAccount.push("le compte lecteur est obligatoire");
}
if (!ldapConnexion.password) {
errors.password.push("le mot de passe est obligatoire");
}
const hasErrors = Object.keys(errors).reduce(
(res, errorType) =>
res || (errors[errorType] && errors[errorType].length > 0),
false
);
cb(errors, hasErrors)
}
in the hooks :
const validateConnexionHook = function (mode = "create") {
return context => {
return new Promise((resolve, reject) => {
const ldapConnexion = context.data || {};
validateConnexion(ldapConnexion, mode, (errors, hasErrors) => {
console.log('**************')
// console.log(errors);
console.log(hasErrors);
if (hasErrors) {
context.error = errors;
reject(errors)
}
else setTimeout(function () { resolve(); }, 3000);
})
})
}
}

Related

How to properly make a promise.all function with a .map?

I attempted to ask a simplified version of this here but I realized that it probably doesn't contain enough information. So unfortunately I'm just going to post the whole thing and hope that it doesn't offend anyone.
Basically I have 4 functions, with their stated purposes below:
1.InitializeDrive() takes a user email and uses Google's JWT user impersonation method to return the appropriate authorization for that user.
2.listfiles() calls on InitializeDrive() for authorization and retrieves a list of the documents associated with the associated user auth.
3.singleUserData() expects a list of the files such as that from listfiles() and refines them.
4.all_user_data() is intended to be the Promise.all async function that combines all of the aforementioned functions, and maps to an array of users getListUsers(), creating a master array containing all of the refined file data for each user.
Basically my poorly formed and ignorant question is 'how can make the all_user_data() in such a way that it will return the aforementioned master array?'. I am relatively new to async programming, and have a persistent problem getting confused with nested functions.
// loads credentials from .env file
require('dotenv').config();
const util = require('util');
const { google } = require('googleapis');
const { logger } = require('handlebars');
const { getListUsers } = require('./get_users');
const target_users = getListUsers();
function initializeDrive(version, user) {
return new Promise((resolve, reject) => {
const client_email = process.env.GOOGLE_CLIENT_EMAIL;
console.log(client_email);
// add some necessary escaping so to avoid errors when parsing the private key.
const private_key = process.env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n');
// impersonate an account with rights to create team drives
const emailToImpersonate = user;
const jwtClient = new google.auth.JWT(
client_email,
null,
private_key,
['https://www.googleapis.com/auth/drive'],
emailToImpersonate,
);
return google.drive({
version: version,
auth: jwtClient,
});
});
}
const listfiles = async (pagetokenObj, user) => {
let pageToken = '';
let version = 'v3';
if (pagetokenObj !== undefined) {
pageToken = pagetokenObj.pageToken;
}
const drive = await initializeDrive(version, user);
//const drive = initializeDrive(version,user);
return new Promise((resolve, reject) => {
drive.files.list(
{
pageSize: 100,
fields:
'nextPageToken, files(id, name, owners(emailAddress),
sharingUser(emailAddress), permissions)',
...(pageToken ? { pageToken } : {}),
},
function (err, { data: { nextPageToken = '', files = [] } = {} }) {
if (err) {
return reject(err);
}
if (!nextPageToken) {
return resolve(files);
}
// if page token is present we'll recursively call ourselves until
// we have a complete file list.
return listfiles({ pageToken: nextPageToken }).then((otherfiles) => {
resolve(files.concat(otherfiles));
});
},
);
});
};
//Function returns formatted
const singleUserData = async (files) => {
return new Promise((resolve, reject) => {
const single_user_json = await listfiles();
const res = single_user_json
.filter((doc) => Boolean(doc.permissions))
.map((doc) => {
return {
id: doc.id,
sharedWith: doc.permissions
.filter((permission) => permission.type === 'user')
.map((permission) => {
return {
emailAddress: permission.emailAddress,
role: permission.role || null,
};
}), // convert the perm object into a string (email address)
};
});
// this is how you get nicer console.logs instead of just [Object] and [Array] BS
// https://stackoverflow.com/a/10729284/9200245
console.log(util.inspect(res, { showHidden: false, depth: null, colors: true }));
if (err) {
return reject(err);
}
if (!nextPageToken) {
return resolve(files);
};
})
};
const all_user_data = async() => {
const users = await getListUsers();
const pagetokenObj = "{ pageToken = '' } = {}";
Promise.all(
users.map(async (pagetokenObj,user) => {
const files = await listfiles(pagetokenObj, user);
console.log(files);
const singleUserData = await singleUserData(files)
console.log(singleUserData);
}),
);
console.log(users)
}
//returnJSON();
//getListUsers();
//singleUserData();
all_user_data();
I finally figured it out. Basically needed to await a lot more and properly declare async.
// loads credentials from .env file
require('dotenv').config();
const util = require('util');
const { google } = require('googleapis');
const { logger } = require('handlebars');
const { getListUsers } = require('./get_users');
const target_users = getListUsers();
async function initializeDrive(user) {
// return new Promise((resolve, reject) => {
const client_email = process.env.GOOGLE_CLIENT_EMAIL;
console.log(user + ': ' + client_email);
// add some necessary escaping so to avoid errors when parsing the private key.
const private_key = process.env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n');
// impersonate an account with rights to create team drives
const emailToImpersonate = await user;
const jwtClient = new google.auth.JWT(
client_email,
null,
private_key,
['https://www.googleapis.com/auth/drive'],
emailToImpersonate,
);
return google.drive({
version: "v3",
auth: jwtClient,
});
//});
}
//
async function listfiles(pagetokenObj, user) {
let pageToken = '';
if (pagetokenObj !== undefined) {
pageToken = pagetokenObj.pageToken;
}
const drive = await initializeDrive(user);
//console.log(drive)
return new Promise((resolve, reject) => {
drive.files.list(
{
pageSize: 100,
fields:
'nextPageToken, files(parents, id, name, properties, owners(emailAddress), sharingUser(emailAddress), permissions)',
...(pageToken ? { pageToken } : {}),
},
function (err, { data: { nextPageToken = '', files = [] } = {} }) {
if (err) {
return reject(err);
}
if (!nextPageToken) {
return resolve(files);
}
// if page token is present we'll recursively call ourselves until
// we have a complete file list.
return listfiles({ pageToken: nextPageToken }).then((otherfiles) => {
resolve(files.concat(otherfiles));
});
},
);
});
};
async function singleUserData(user) {
const pagetokenObj = "{ pageToken = '' } = {}"
const single_user_json = await listfiles(pagetokenObj, user);
// return new Promise ((resolve, reject) => {
console.log(user+ ":" + JSON.stringify(single_user_json));
const res = await single_user_json
.filter((doc) => Boolean(doc.permissions))
.map((doc) => {
return {
id: doc.id,
sharedWith: doc.permissions
.filter((permission) => permission.type === 'user')
.map((permission) => {
return {
emailAddress: permission.emailAddress,
role: permission.role || null,
};
}) // convert the perm object into a string (email address)
};
});
return JSON.stringify(res);
//})
}
async function getAllUserData() {
try {
const users = await getListUsers();
// const userString = JSON.parse(users);
console.log("test22222222222222: " + users)
const usersData = await Promise.all(
users.map((user) => {
return singleUserData(user);
})
);
console.log("[" + usersData + "]");
return usersData;
} catch (error) {
console.log(error);
}
}
getAllUserData();
You're almost there.
Remove listfiles call in all_user_data
Pass user to singleUserData
Remove pagetokenObj in all_user_data
Remove async and await inside users.map and return promise directly
Await Promise.all, assign it to a variable and return that variable
Remove unnecessary Promise wrapping in singleUserData
Change function signature of singleUserData to take in a user
Pass user to listfiles in singleUserData
Return res instead of files in singleUserData
Here are the changes I made to all_user_data and singleUserData:
// Function returns formatted
async function singleUserData(user) {
const single_user_json = await listfiles(user);
const res = single_user_json
.filter((doc) => Boolean(doc.permissions))
.map((doc) => {
return {
id: doc.id,
sharedWith: doc.permissions
.filter((permission) => permission.type === "user")
.map((permission) => {
return {
emailAddress: permission.emailAddress,
role: permission.role || null,
};
}), // convert the perm object into a string (email address)
};
});
return res;
};
async function getAllUserData() {
const users = await getListUsers();
const usersData = await Promise.all(
users.map((user) => {
return singleUserData(user);
})
);
return usersData;
};
const usersData = await getAllUserData();

res.json returns undefined from express server after retrieving data from firebase realtime database

I currently have a realtime database set up that saves a list of a specific user's list of saved jobs for a job search application with react on the front end. However, when I try to return that data through res.json, console.logging it prints out undefined. Would appreciate any help or insight on why it is behaving this way and/or possible fixes.
Thanks in advance.
The data looks something like this:
{
"users" : {
"<userId>" : {
"savedJobs" : [ {
"company" : "PepsiCo",
"id" : 7693578,
"location" : [ {
"name" : "Valhalla, NY"
} ]
} ]
}
}
}
It is initialized like so:
var admin = require("firebase-admin");
let db = admin.database();
var userRef = db.ref("users");
Old Backend logic:
app.get("/fetchSavedJobs/:userId", (req, res) => {
console.log("inside fetch saved jobs");
console.log("fetch saved jobs user id " + req.params.userId);
const userId = req.params.userId;
let list = [];
userRef.orderByChild(userId).limitToLast(1).once('value').then((querySnapshot) => {
if(!querySnapshot.numChildren) {
throw new Error("user not in database, no saved jobs");
}
let dataSnapshot;
querySnapshot.forEach((snap) => {
dataSnapshot = snap;
})
if (!dataSnapshot.exists()) { // value may be null, meaning idToFind doesn't exist
throw new Error(`Entry ${userId} not found.`);
}
const jobsList = dataSnapshot.val().savedJobs;
jobsList.forEach((x) => list.push({company: x.company, id: x.id}));
return res.json(list);
})
I've re-edited my back-end to to the following:
console.log(userId, typeof userId)
prints "userId_value, string"
app.get("/fetchSavedJobs/:userId", (req, res) => {
const userId = req.params.userId;
var userRef = db.ref("users/" + userId); //initializing userRef
let list = [];
userRef.once("value").then((snapshot) => {
console.log("snapshot val", snapshot.val());
res.json(snapshot.val().savedJobs);
})
Front end:
export const fetchSavedJobs = (userUid) => {
return async (dispatch) => {
const fetchData = async () => {
console.log("fetch data is called");
const response = await fetch("/fetchSavedJobs/" + userUid, {
method: "GET",
headers: {
'Accept': 'application/json',
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error("Fetching saved cart data failed");
}
};
try {
const savedJobsData = await fetchData();
console.log("came back from server with saved jobs");
console.log("retrieved Saved Jobs data" + savedJobsData); //this prints out undefined
} catch (error) {}
};
};

I need help to get a simple query for MongoDB (SQL equivalent: "select speed from values")

I need help to get a simple query for MongoDB (SQL equivalent: "select speed from values"). But I can't find a solution for that.
Node.js backend for Vue.js
// a Part of Init-function:
await collection.replaceOne({}, {
speed: {
value: 65,
type: 2,
},
eightSpeed: {
value: 0.0043,
type: 2,
},
oSpeed: {
value: 0.31,
type: 2,
},
minusSpeed: {
value: 2.42,
type: 2,
}
}
//...
Now I need the query like this: http://192.168.220.220:3000/settings/getValue/speed to get an object speed.
const express = require("express");
const mongodb = require("mongodb");
const SettingsRouter = new express.Router();
SettingsRouter.get("/getValue/:value", async function (req, res) {
try {
const val = req.params.value; // req.body.text;
const select = {};
select[`${val}.value`] = 1;
console.log("settings.js:", "/getValue/:name", select);
const collection = await loadMongoCollection();
const value = await collection.findOne({},select)//.toArray();
res.status(201).send(value);
} catch (error) {
res.status(500).send("Failed to connect Database");
console.error("settings.js:", "/getValue/:name:", error);
}
});
async function loadMongoCollection() {
const dbUri = process.env.MONGODB_CONNSTRING || config.dbUri;
const MongoDB = process.env.MONGODB_DB || config.db;
try {
const client = await mongodb.MongoClient.connect(dbUri, {
useNewUrlParser: true,
});
const conn = client.db(MongoDB).collection("values");
return conn;
} catch (error) {
console.error("settings.js:", "loadMongoCollection:", error);
}
}
When I try it, I get all (not only the Speed Object) what I expected, or nothing.
What I do wrong?
EDIT 2022.01.06:
I try it to change my database:
data = {
speed: {
value: 65,
type: 2,
},//...
}
//...
await collection.replaceOne({}, {values:data}, { upsert: true });
And then the query:
const select = {[`values.${val}.value`]:"1"};
const where = {}; //{"values":val};
const value = await collection.find(where,select,).toArray();
But it will not work in rest... is there an issue in mongo package?
When I do it in https://cloud.mongodb.com - it works:
But my request returns all values...
Log shows:"'settings.js:', '/getValue/:name', { 'values.speed.value': '1' }"
In the screenshot you show the projection is values.speed.value, but in your endpoint you have:
const select = {};
select[`${val}.value`] = 1;
Which would evaluate to speed.value. To mimic what you have on the MongoDB console this should be:
const select = {};
select[`values.${val}.value`] = 1;
So, I changed my Frondend (vue) to spred all Values. I maked some Helper Functions:
CheckForChangedObj
check two object for changes
updateObj
copy all data from object into another
export function CheckForChangedObj(targetObject, obj) {
if (targetObject === typeof undefined || targetObject == {}) {
return true
}
if (JSON.stringify(targetObject) !== JSON.stringify(obj)) {
return true
}
return false
}
export function updateObj(targetObject, obj) {
let keys = Object.keys(obj)
for (let k in keys) {
try {
let key = keys[k]
if (!targetObject.hasOwnProperty(key)) {
//define target, if not exist
targetObject[key] = {}
}
//Workaround for References
if (obj[key].hasOwnProperty("__v_isRef")) {
if (targetObject[key].hasOwnProperty("__v_isRef")) {
targetObject[key].value = obj[key].value
} else {
targetObject[key] = obj[key].value
}
} else {
//Source i a Norm Value
//check for an Object inside, then go in...
if ("object" === typeof obj[key] && !Array.isArray(obj[key])) {
updateObj(targetObject[key], obj[key]) // recurse
} else {
//not else-if!!! __v_isRef: true
if (targetObject[key].hasOwnProperty("__v_isRef")) {
targetObject[key].value = obj[key]
} else {
targetObject[key] = obj[key]
}
}
}
} catch (error) {
console.log(
"key:",
keys[k],
"Error:",
error
)
}
}
}
Then spreading...
import {updateObj,CheckForChangedObj } from "../tools/tools"
beforeMount() {
this.getAllValuesAPI()
setInterval(() => {
this.checkForChanges()
// ml("TABLE", this.values)
}, 10000)
},
setup() {
let prevValues = {}
let values = {
speed: {
id:1,
type: SettingType.Slider,
value: ref(0)
}
//...
}
function checkForChanges() {
//intervaled function to get changes from API
let changed = CheckForChangedObj(this.values, this.prevValues)
if (changed) {
updateObj(this.prevValues, this.values)
setValuesToAPI()
}
else{
getAllValuesFromAPI()
}
}
async function getAllValuesFromAPI() {
//get ALL Settings-Data from API, and put them in values
const res = await axios.get(`settings/getAll`)
return updateObj(this.values, res.data[0].values) u
}
}
When you have a better solution, please help...

Handling result of Multiple async call and call the database after collecting result

Guys, I am new in node js and I am trying to do below steps
1) Calling the AWS API to create the Cognito user by passing data.
2)when all the request will be completed then i will insert all the record in the database.
3) user is the array of all the users.
Here is what I have done
const obj = new ReadCsvFile();
obj.readCSVFromAWS()
.then(result => {
const user = obj.getMigratedList();
for (const i in user) {
if (user[i] !== null && user[i] !== undefined) {
const uuid = obj.createUserInCognito(user[i]);
uuid.then(userAttribute => {
user[i].uuid = String(userAttribute.User.Attributes.values); //should complete all the request
});
}
}
})
.catch(err => {
console.log(err);
});
public async createUserInCognito(data: User) {
const CognitoIdentityServiceProvider = AWS.CognitoIdentityServiceProvider;
const client = new CognitoIdentityServiceProvider({ apiVersion: "2016-04-19" });
const params = {
UserPoolId: "us-east-2_lleSjp1bN" /* required */,
Username: data.email /* required */,
DesiredDeliveryMediums: ["EMAIL"],
ForceAliasCreation: false,
// email_verified: true,
// MessageAction: "SUPPRESS",
TemporaryPassword: data.password,
UserAttributes: [
{
Name: "email" /* required */,
Value: data.email
}
]
};
return await client.adminCreateUser(params).promise();
}
Problem
1) I want that all the request should complete of Cognito user.
2) Then I need to pass the list of users into the database.
3) I want to know how can i wait to complete all the request and then insert into the database.
Please help.
Use the code snippet written below :
const obj = new ReadCsvFile();
obj.readCSVFromAWS()
.then(result => {
const user = obj.getMigratedList();
for (const i in user) {
if (user[i] !== null && user[i] !== undefined) {
obj.createUserInCognito(user[i]).then(uuid=>{
uuid.then(userAttribute => {
user[i].uuid = String(userAttribute.User.Attributes.values); //should complete all the request
});
});
}
}
})
.catch(err => {
console.log(err);
});

koa2+koa-router+mysql keep returning 'Not Found'

Background
I am using koa2 with some middlewares to build a basic api framework. But when I use "ctx.body" to send response in my router, the client side always receive "Not Found"
My code
./app.js
const Koa = require('koa');
const app = new Koa();
const config = require('./config');
//Middlewares
const loggerAsync = require('./middleware/logger-async')
const bodyParser = require('koa-bodyparser')
const jsonp = require('koa-jsonp')
app.use(loggerAsync())
app.use(bodyParser())
app.use(jsonp());
//Router
const gateway = require('./router/gateway')
app.use(gateway.routes(), gateway.allowedMethods());
app.use(async(ctx, next) => {
await next();
ctx.response.body = {
success: false,
code: config.code_system,
message: 'wrong path'
}
});
app.listen(3000);
./router/gateway.js
/**
* Created by Administrator on 2017/4/11.
*/
const Router = require('koa-router');
const gateway = new Router();
const df = require('../db/data-fetcher');
const config = require('../config');
const moment = require('moment');
const log4js = require('log4js');
// log4js.configure({
// appenders: { cheese: { type: 'file', filename: 'cheese.log' } },
// categories: { default: { appenders: ['cheese'], level: 'error' } }
// });
const logger = log4js.getLogger('cheese');
logger.setLevel('ERROR');
gateway.get('/gateway', async(ctx, next) => {
let time = ctx.query.time;
if (!time) {
ctx.body = {
success: false,
code: config.code_system,
message: 'Please input running times'
}
} else {
try {
let r = await df(`insert into gateway (g_time, g_result, g_date) values (${time}, '',now())`);
return ctx.body = {
success: true,
code: config.code_success
}
} catch (error) {
logger.error(error.message);
}
}
});
module.exports = gateway;
Then a db wrapper(mysql)
./db/async-db.js
const mysql = require('mysql');
const config = require('../config');
const pool = mysql.createPool({
host: config.database.HOST,
user: config.database.USERNAME,
password: config.database.PASSWORD,
database: config.database.DATABASE
})
let query = (sql, values) => {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) {
reject(err)
} else {
connection.query(sql, values, (err, rows) => {
if (err) {
reject(err)
} else {
resolve(rows)
}
connection.release()
})
}
})
})
}
module.exports = query
./db/data-fetcher.js
const query = require('./async-db')
async function performQuery(sql) {
let dataList = await query(sql)
return dataList
}
module.exports = performQuery;
My running result
When I launch server on port 3000 then accesss via http://localhost:3000/gateway?time=5, it always returns "Not found". But as I can see I have already used
return ctx.body = {
success: true,
code: config.code_success
}
to send response. I debugged and found that the database processing was done well, the new data was inserted well.
when I remove that db inserting line, it works well and returns success info.
let r = await df(`insert into gateway (g_time, g_result, g_date) values (${time}, '',now())`);
Is there anything wrong?
Thanks a lot!
Update 2017/04/27
Now I have found the problem. It's due to my custom middleware
const loggerAsync = require('./middleware/logger-async')
Code are like following -
function log( ctx ) {
console.log( ctx.method, ctx.header.host + ctx.url )
}
module.exports = function () {
return function ( ctx, next ) {
return new Promise( ( resolve, reject ) => {
// 执行中间件的操作
log( ctx )
resolve()
return next()
}).catch(( err ) => {
return next()
})
}
}
I changed it to async/await way then everything is working well.
Could anyone please tell me what's wrong with this middleware?
I guess, your problem is the ./db/data-fetcher.js function. When you are calling
let r = await df(`insert ....`)
your df - function should return a promise.
So try to rewrite your ./db/data-fetcher.js like this (not tested):
const query = require('./async-db')
function performQuery(sql) {
return new Promise((resolve, reject) => {
query(sql).then(
result => {
resolve(result)
}
)
}
}
module.exports = performQuery;
Hope that helps.
correct middleware:
function log( ctx ) {
console.log( ctx.method, ctx.header.host + ctx.url )
}
module.exports = function () {
return function ( ctx, next ) {
log( ctx );
return next()
}
}
reason: when resolve involved; promise chain was completed; response has been sent to client. although middleware remained will involved, but response has gone!
try to understand It seems that if you want to use a common function as middleware, you have to return the next function
nodejs(koa):Can't set headers after they are sent

Resources