How to store DNS output in a variable in nodejs - javascript

How do I store the return value of dns.lookup in a variable? If I store it in an array it shows blank.
Following is the code I'm working on:
const dns = require('dns');
class Network
{
search(host)
{
let options = {
hints: dns.ADDRCONFIG | dns.V4MAPPED,
all: true,
verbatim: true
}
let addr = [];
dns.lookup(host, options, (error, address) =>
{
if(error)
{
console.log("Could not resolve %s\n", host);
console.log(error);
}
address.forEach(ip => {
addr.push({
address: ip.address,
family: ip.family
});
});
});
console.log(addr);
return addr;
}
}
const network = new Network();
let output = network.search("www.google.com");
console.log(output);
The output of the code is Blank i.e. []
Please suggest a rememdy.
Thanks

I would suggest using the async/await syntax along with the very useful util.promisify function.
This gives us code that is more compact, it will also allow you to write your code in a way that's very similar to synchronous code.
We'll then use some destructuring to pick only the properties in the address object(s) we're interested in.
const dns = require('dns');
const promisify = require('util').promisify;
class Network
{
async search(host)
{
let options = {
hints: dns.ADDRCONFIG | dns.VMAPPED,
all: true,
verbatim: true
}
let address = await promisify(dns.lookup)(host, options);
return address.map( ({address, family}) => {
return { address, family };
});
}
}
async function testSearch() {
const network = new Network();
let output = await network.search("www.google.com");
console.log(output);
}
testSearch();

The last parameter to dns.lookup must be a callback as you have already noticed. The method is asynchronous but this line return addr; is not - addr is returned before getting populated.
The method definition of search could be changed to take as second parameter a callback that will be called with the result of dns.lookup.
Something like this:
search(host, done) {
...
dns.lookup(host, options, (err, addresses) => {
...
const finalAddresses = [];
addresses.forEach((ip) => {
finalAddresses.push({
address: ip.address,
family: ip.family
});
});
done(null, finalAddresses);
});
}
const network = new Network();
network.search("www.google.com", (err, result) => {
if (err) {
return console.log(err);
}
console.log(result);
});

Related

Nestjs Testing in signup BadRequestException: email in use error

user.service.ts
async findWithMail(email:string):Promise<any> {
return this.userRepository.findOne({email});
}
auth.service.ts
async signup(email:string,password:string,name?:string,surname?:string,phone:string){
if(email) {
const users = await this.userService.findWithMail(email);
if(users) {
throw new BadRequestException('email in use');
}
}
if(!password) return {error:"password must be!"};
const salt = randomBytes(8).toString('hex');
const hash = (await scrypt(password,salt,32)) as Buffer;
const result = salt + '.' +hash.toString('hex');
password = result;
const user = await
this.userService.create(email,password,name,surname,phone);
return user;
}
auth.service.spec.ts
let service:AuthService;
let fakeUsersService: Partial<UserService>;
describe('Auth Service',()=>{
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) => Promise.resolve({email,password} as User),
}
const module = await Test.createTestingModule({
providers:[AuthService,{
provide:UserService,
useValue:fakeUsersService
}],
}).compile();
service = module.get(AuthService);
});
it('can create an instance of auth service',async()=> {
expect(service).toBeDefined();
})
it('throws an error if user signs up with email that is in use', async () => {
await service.signup('asdf#asdf.com', 'asdf')
});
})
When ı try to run my test its give me error even this email is not in database its give error: BadRequestException: email in use. I couldnt figure out how to solve problem
You can use isExists method instead of findOne.
Also you can add extra check for your findWithMail method. Check the length of db request result. Like if (dbReqResult.length === 0) return false; else true
please put your attention on your mocked user service, especially on findWithEmail function, this part
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) =>
Promise.resolve({email,password} as User),
}
...
try to resolve the promise to be null not [] (empty array) or change your if(users) on your auth.service to be if(users.length > 0), why? it because empty array means to be thruthy value so when run through this process on your auth.service
if(email) {
const users = await this.userService.findWithMail(email);
// on this part below
if(users) {
throw new BadRequestException('email in use');
}
}
the 'users' executed to be truthy value so it will invoke the error. I hope my explanation will help you, thank you

Getting "MongoExpiredSessionError: Cannot use a session that has ended" when using forEach to update document but why?

I'm just trying to upsert a key value pair i.e. {mark : done} to my Mongo database using id that I have stored in an array.
But for the life of me I can't figure out why if I use a for loop it works fine. But if I use a forEach loop, it gives me a "Cannot use a session that has ended" Error.
I'm hoping someone SO assist in explaining why this happens
Boiler Plate Code
import { MongoClient } from "mongodb";
const uri = "mongodb://localhost:27018/";
const client = new MongoClient(uri);
Function with "for loop" implementation
async function updateMongo1() {
const db = client.db("testDB");
const coll = db.collection("testCollection");
await client.connect();
try {
let array = [
"2021-10-18",
"2021-10-17",
"2021-10-16",
"2021-10-15",
"2021-10-14",
];
for (let i = 0; i < array.length; i++) {
await coll.updateOne(
{ _id: array[i] },
{ $set: { mark: "done" } },
{ upsert: true }
);
}
} catch (e) {
console.error(e.message);
} finally {
await client.close();
}
}
Function with "forEach" implementation
async function updateMongo2() {
const db = client.db("testDB");
const coll = db.collection("testCollection");
await client.connect();
try {
let array = [
"2021-10-18",
"2021-10-17",
"2021-10-16",
"2021-10-15",
"2021-10-14",
];
array.forEach(async (element) => {
await coll.updateOne(
{ _id: element },
{ $set: { mark: "done" } },
{ upsert: true }
);
});
} catch (e) {
console.error(e.message);
} finally {
await client.close();
}
}
running different implementations yields different results.
(async () => {
//await updateMongo1(); //-> this works
await updateMongo2(); //-> this gives "MongoExpiredSessionError: Cannot use a session that has ended" what gives??
})();
Please let me know why this happens?
The element variable is an array e.g. ["2021-10-18"]. In the second implementation, you were meant to access element[0] but forgot to so you put an array into the _id field.
I don't know if that's what's specifically causing your error but it is an error in the implementation.

oracledb node for node-js (several queries one after the other). The other take result as binds parameter

I am stuck at my problem because I am really new to oracle-db for node-js and JavaScript in general.
I wrote API-endpoints with JavaScript and each endpoint does an oracledb “select query”. However, my colleagues need only one endpoint which gives all results of all oracle select-queries as one JSON object. All queries are dependent on the first query, i.e. the result of the first query has to be used as a bind-parameter for the second query and so forth.
In this js-file I tried to execute two oracle-sql queries (one after the other) and use one value of the result-JSON-object of the first query as a parameter for the second query and give all results as response for the API client. But that does not work. What am I doing wrong here? With only one sql and therefore only one result it works. Thanks in advance!
Here is my code I wrote:
const oracledb = require('oracledb');
const config = require('../config/config')
oracledb.autoCommit = true
oracledb.fetchAsString = [ oracledb.DATE, oracledb.NUMBER ]
module.exports= async (req, res) => {
let connection;
try {
var sql, binds, options, result, result2, time
connection = await oracledb.getConnection(config.oracledbconnection)
// Query the data
sql = `SELECT FIRSTNr, SECONDNR,STARTTIME,MACHINE FROM MACHINELOGS WHERE Machine=:Machine AND ENDTIME > CURRENT_DATE -1 ORDER BY RUNNO ASC`;
binds = {Machine:req.params.machine}
options = {
outFormat: oracledb.OUT_FORMAT_OBJECT // query result format
//outFormat: oracledb.OBJECT // is the same as above // extendedMetaData: true, // get extra metadata
// prefetchRows: 100, // internal buffer allocation size for tuning
// fetchArraySize: 100 //internal buffer allocation size for tuning
};
result = await connection.execute(sql, binds, options);
// console.log("Metadata: ");
// console.dir(result.metaData, { depth: null });
// console.log("Query results: ");
// console.dir(result.rows, { depth: null });
//
// Show the date. The value of ORA_SDZT affects the output
sql = `SELECT TO_CHAR(CURRENT_DATE, 'DD-Mon-YYYY HH24:MI') AS CD FROM DUAL`;
time = await connection.execute(sql, binds, options);
// console.log("Current date query results: ");
// console.log(result2.rows[0]['CD']);
sql = `SELECT GRADE FROM NOTES WHERE SECONDNR=:Secondnr`
binds = {Secondnr:result.rows[0]['SECONDNR']}
result2 = await connection.execute(sql,binds,options);
options = {
outFormat: oracledb.OUT_FORMAT_OBJECT //
};
}
catch (err)
{
console.error(err);
}
finally
{
if (connection)
{
try {
await connection.close();
}
catch (err) {
console.error(err);
}
}
}
res.send(result,result2)
}
Take it back to basics. This works:
'use strict';
process.env.ORA_SDTZ = 'UTC';
const oracledb = require('oracledb');
const dbConfig = require('./dbconfig.js');
if (process.platform === 'darwin') {
oracledb.initOracleClient({libDir: process.env.HOME + '/Downloads/instantclient_19_8'});
}
let sql, options, result1, result2;
options = { outFormat: oracledb.OUT_FORMAT_OBJECT };
async function run() {
let connection;
try {
connection = await oracledb.getConnection(dbConfig);
result1 = await connection.execute(
`select 1 as myval from dual`,
[], options);
console.dir(result1.rows, { depth: null });
result2 = await connection.execute(
`select sysdate from dual where 1 = :bv`,
{bv: result1.rows[0].MYVAL},
options);
console.dir(result2, { depth: null });
} catch (err) {
console.error(err);
} finally {
if (connection) {
try {
await connection.close();
} catch (err) {
console.error(err);
}
}
}
}
run();
Then try with your datatypes.
I'm not sure what you want to do with returning 'all results'. You can build up any JS object and convert to and from JSON easily with JSON.stringify and JSON.parse.
For efficiency, you could review whether you can do all your 'queries' in a single SQL statement.

Asynchronous verification within the .map function

I am developing the backend of an application using Node JS, Sequelize and Postgres database.
When the course is registered, the user must inform which organizations, companies and teachers will be linked to it.
The organization IDs are passed through an array to the backend, I am trying to do a check to make sure that the passed IDs exist.
What I've done so far is this:
const { organizations } = req.body;
const organizationsArray = organizations.map(async (organization) => {
const organizationExists = await Organization.findByPk(organization);
if (!organizationExists) {
return res
.status(400)
.json({ error: `Organization ${organization} does not exists!` });
}
return {
course_id: id,
organization_id: organization,
};
});
await CoursesOrganizations.bulkCreate(organizationsArray);
This link has the complete controller code, I believe it will facilitate understanding.
When !OrganizationExists is true, I am getting the return that the organization does not exist. The problem is when the organization exists, I am getting the following message error.
The Array.map() is returning an array of promises that you can resolve to an array using Promise.all(). Inside the map you should use throw new Error() to break out of the map - this error will be raised by Promise.all() and you can then catch it and return an error to the client (or swallow it, etc).
This is a corrected version of your pattern, resolving the Promise results.
const { organizations } = req.body;
try {
// use Promise.all to resolve the promises returned by the async callback function
const organizationsArray = await Promise.all(
// this will return an array of promises
organizations.map(async (organization) => {
const organizationExists = await Organization.findByPk(organization, {
attributes: ['id'], // we only need the ID
raw: true, // don't need Instances
});
if (!organizationExists) {
// don't send response inside the map, throw an Error to break out
throw new Error(`Organization ${organization} does not exists!`);
}
// it does exist so return/resolve the value for the promise
return {
course_id: id,
organization_id: organization,
};
})
);
// if we get here there were no errors, create the records
await CoursesOrganizations.bulkCreate(organizationsArray);
// return a success to the client
return res.json({ success: true });
} catch (err) {
// there was an error, return it to the client
return res.status(400).json({ error: err.message });
}
This is a refactored version that will be a bit faster by fetching all the Organizations in one query and then doing the checks/creating the Course inserts.
const { Op } = Sequelize;
const { organizations } = req.body;
try {
// get all Organization matches for the IDs
const organizationsArray = await Organization.findAll({
attributes: ['id'], // we only need the ID
where: {
id: {
[Op.in]: organizations, // WHERE id IN (organizations)
}
},
raw: true, // no need to create Instances
});
// create an array of the IDs we found
const foundIds = organizationsArray.map((org) => org.id);
// check to see if any of the IDs are missing from the results
if (foundIds.length !== organizations.length) {
// Use Array.reduce() to figure out which IDs are missing from the results
const missingIds = organizations.reduce((missingIds, orgId) => {
if (!foundIds.includes(orgId)){
missingIds.push(orgId);
}
return missingIds;
}, []); // initialized to empty array
throw new Error(`Unable to find Organization for: ${missingIds.join(', ')}`);
}
// now create an array of courses to create using the foundIds
const courses = foundIds.map((orgId) => {
return {
course_id: id,
organization_id: orgId,
};
});
// if we get here there were no errors, create the records
await CoursesOrganizations.bulkCreate(courses);
// return a success to the client
return res.json({ success: true });
} catch (err) {
// there was an error, return it to the client
return res.status(400).json({ error: err.message });
}
If you have an array of Ids and you want to check if they exist you should you use the (in) operator, this makes it so that you are hitting the DB only once and getting all the records in one hit (instead of getting them one by one in a loop), after you get these records you can check their lengths to determine if they all exist or not.
const { Op } = require("sequelize");
let foundOrgs = await Organization.findAll({
where: {
id: {
[Op.in]: organizationsArray,
}
}
});

Node.js: Awaiting a Require

I'm new the Node.js and I've been working with a sample project by a third party provider and I'm trying to use Azure Key Vault to store configuration values.
I'm having trouble getting a process to wait before executing the rest. I'll try to detail as much as I know.
The sample project has a file named agent.js which is the start page/file. On line 16 (agent_config = require('./config/config.js')[process.env.LP_ACCOUNT][process.env.LP_USER]) it calls a config file with values. I'm trying to set these value using Key Vault. I've tried many combinations of calling functions, and even implementing async / await but the value for agent_config always contains a [Promise] object and not the data returned by Key Vault.
If I'm right, this is because the Key Vault itself uses async / await too and the config file returns before the Key Vault values are returned.
How can Key Vault be added/implemented in a situation like this?
Here's what I've tried:
First updated agent.js to
let agent_config = {};
try {
agent_config = require('./config/config.js')['123']['accountName'];
} catch (ex) {
log.warn(`[agent.js] Error loading config: ${ex}`)
}
console.log(agent_config);
Test 1
./config/config.js
const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
const KEY_VAULT_URI = 'https://' + '{my vault}' + '.vault.azure.net/' || process.env['KEY_VAULT_URI'];
function getValue(secretName, secretVersion) {
msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' }).then((credentials) => {
const client = new KeyVault.KeyVaultClient(credentials);
client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
function (response) {
return response.Value;
});
});
}
module.exports = {
'123': {
'accountName': {
accountId: getValue('mySecretName', '')
}
}
};
Results
{ accountsId: undefined }
Test 2
Made getValue an async function and wrapped it around another function (tried without the wrapping and didn't work either)
./config/config.js
const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
const KEY_VAULT_URI = 'https://' + '{my vault}' + '.vault.azure.net/' || process.env['KEY_VAULT_URI'];
async function getValue(secretName, secretVersion) {
msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' }).then((credentials) => {
const client = new KeyVault.KeyVaultClient(credentials);
client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
function (response) {
return response.Value;
});
});
}
async function config() {
module.exports = {
'123': {
'accountName': {
accountId: await getValue('mySecretName', '')
}
}
};
}
config();
Results
{}
Test 3
Made getValue an async function and wrapped it around another function (tried without the wrapping and didn't work either)
./config/config.js
const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
const KEY_VAULT_URI = 'https://' + '{my vault}' + '.vault.azure.net/' || process.env['KEY_VAULT_URI'];
async function getValue(secretName, secretVersion) {
return msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' })
.then((credentials) => {
const client = new KeyVault.KeyVaultClient(credentials);
return client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
function (response) {
return response.Value;
});
});
}
module.exports = {
'123': {
'accountName': {
accountId: getValue('mySecretName', '')
}
}
};
config();
Results
{ accountId: { <pending> } }
Other
I've tried many others ways like module.exports = async (value) =< {...} (found through other questions/solutions without success.
I'm starting to think I need to do some "waiting" on agent.js but I haven't found good info on this.
Any help would be great!
One issue is that your getValue function is not returning anything as your returns need to be explicit.
(and without the promise being returned, there's nothing to await on)
async function getValue(secretName, secretVersion) {
return msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' })
.then((credentials) => {
const client = new KeyVault.KeyVaultClient(credentials);
return client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
function (response) {
return response.Value;
});
});
}
You could also get away with less explicit returns using arrow functions..
const getValue = async (secretName, secretVersion) =>
msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' })
.then(credentials => {
const client = new KeyVault.KeyVaultClient(credentials);
return client.getSecret(KEY_VAULT_URI, secretName, secretVersion)
.then(response => response.Value);
});
Introducing the Azure Key Vault read, which is async, means your whole config read is async. There' nothing you can do to get around that. This will mean that the code that uses the config will need to handle it appropriately. You start by exporting an async function that will return the config..
async function getConfig() {
return {
'123': {
'accountName': {
accountId: await getValue('mySecretName', '')
}
}
};
}
module.exports = getConfig;
In your agent code you call that function. This will mean that your agent code will need to be wrapped in a function too, so maybe something like this..
const Bot = require('./bot/bot.js');
const getConfig = require('./config/config.js');
getConfig().then(agentConfig => {
const agent = new Bot(agentConfig);
agent.on(Bot.const.CONNECTED, data => {
log.info(`[agent.js] CONNECTED ${JSON.stringify(data)}`);
});
});
The package azure-keyvault has been deprecated in favor of the new packages to deal with Keyvault keys, secrets and certificates separately. For your scenario, you can use the new #azure/keyvault-secrets package to talk to Key Vault and the new #azure/identity package to create the credential.
const { SecretClient } = require("#azure/keyvault-secrets");
const { DefaultAzureCredential } = require("#azure/identity");
async function getValue(secretName, secretVersion) {
const credential = new DefaultAzureCredential();
const client = new SecretClient(KEY_VAULT_URI, credential);
const secret = await client.getSecret(secretName);
return secret.value;
}
The DefaultAzureCredential assumes that you have set the below env variables
AZURE_TENANT_ID: The tenant ID in Azure Active Directory
AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant
AZURE_CLIENT_SECRET: The client secret for the registered application
To try other credentials, see the readme for #azure/identity
If you are moving from the older azure-keyvault package, checkout the migration guide to understand the major changes

Categories

Resources