Error Export Module in Node.js - separation of concerns - javascript

I am trying to implement separation of concerns by using export module. All the code is working if used without separation of concern but as soon as I am trying to import generateUrlArray() from const db = require('../db') nothing is working. Nodejs is not giving me any error on the back-end. The error I am getting on front-end is Error: SyntaxError: Unexpected end of JSON input . I am positive that the error is coming from back-end. Let me know if you have any ideas.
controller.js
const db = require('../db')
exports.getWebApiList = (req, res) => {
(async function fetchDataList() {
try {
const urlArray = await db.generateUrlArray({}, { _id: 0 })
return res.send(urlArray)
} catch (ex) {
console.log(`fetchDataList error: ${ex}`)
}
})()
}
..db/index.js
const { List } = require('./models/List')
const generateUrlArray = (query, projection) => {
const dataFromDB = List.find(query, projection).select('symbol')
return linkArray = dataFromDB.map(item => {
return link = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${item.symbol}&apikey=6BUYSS9QR8Y9HH15`
})
}
module.exports = { generateUrlArray }
.models/List.js
const mongoose = require('mongoose')
mongoose.Promise = global.Promise
const ParentSchemaSymbolList = new mongoose.Schema({
symbol: String
})
module.exports.List = mongoose.model('List', ParentSchemaSymbolList)

const generateUrlArray = async (query, projection) => {
const dataFromDB = await List.find(query, projection).select('symbol')
const linkArray = dataFromDB.map(item => {
return link = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${item.symbol}&apikey=6BUYSS9QR8Y9HH15`
})
return linkArray
}

Related

Nojde does not await for async function and array.length condition

Trying to implement Twitter API to post tweets with multiple images. I am posting requests from the admin dashboard with an AD id(not the Twitter ad) , fetching the images URL from our database and using the URLs to write image files in the upload directory. Then using the Twitter-api-2 package to post a request to Twitter API to get the mediaIdS and post the tweet.
Problem: When I write files to local upload folders, the async function also get executed, therefore cannot find the media files in the local folder, leading to an error.
const router = require('express').Router()
const { parse } = require('dotenv');
const { link } = require('joi');
const { TwitterApi } = require('twitter-api-v2')
const { FetchSingleAdBasics } = require('../helpers/fetch-single-ad-basics');
const request = require('request');
const fs = require('fs');
const path = require('path');
const https = require('https')
function saveImagesToUploads(url, path){
const fullUrl = url
const localPath = fs.createWriteStream(path)
const request = https.get(fullUrl, function(response){
console.log(response)
response.pipe(localPath)
})
}
var jsonPath1 = path.join(__dirname,'../..','uploads/0.png');
var jsonPath2 = path.join(__dirname,'../..','uploads/1.png');
var jsonPath3= path.join(__dirname,'../..','uploads/2.png');
var jsonPath4 = path.join(__dirname,'../..','uploads/3.png');
router.post('/twitter-post', async(req, res) => {
const {adId} = req.body
const imagesArr = []
const imageIdsArr = []
const {text} = req.body
const AD = adId && await FetchSingleAdBasics(adId);
const PostMessage = `${AD?.year} ${AD?.make} ${AD?.model} ${AD?.trim}\r\n${AD?.engineSize}L Engine\r\nPrice: AED${AD?.addetails[0].price}\r\nMileage: ${AD?.mileage} - ${AD?.mileageUnit}\r\nMechanical Condition: ${AD?.addetails[0].mechanicalCondition}\r\nAvailable in: ${AD?.adcontacts[0]?.location}\r\nCheckout full details at: https://ottobay.com/cars/uc/${AD?.id}`
if (!AD)
return res
.status(422)
.json({ message: "failed", error: "Ad Not Found" })
try {
imagesArr.push(await AD?.adimages[0]?.LeftSideView)
imagesArr.push(await AD?.adimages[0]?.LeftFront)
imagesArr.push(await AD?.adimages[0]?.Front)
imagesArr.push(await AD?.adimages[0]?.FrontRight)
// the following function must await for this to finish
imagesArr?.map((item,index) => {
saveImagesToUploads(item, "./uploads/" + `${index}`+ '.png')
})
const filesArr = [jsonPath1,jsonPath3,jsonPath4,jsonPath2]
console.log(filesArr)
console.log(filesArr?.length)
const idsArray = []
// this function get executed without waiting for previous function, leading to error
// this function does not apply filesArr?.length === 4 condition
filesArr?.length === 4 && await Promise.all(filesArr?.length === 4 && filesArr?.map(async (item) => {
try {
const mediaId = await client.v1.uploadMedia(item,{ mimeType : 'png' })
idsArray.push(mediaId)
return imageIdsArr;
} catch(err) {
console.log(err)
throw err;
}
}));
const response = idsArray?.length === 4 && await client.v1.tweetThread([{ status: PostMessage, media_ids: idsArray }]);
// remove files after successfull tweet
await fs.promises.readdir(jsonUploadPath).then((f) => Promise.all(f.map(e => fs.promises.unlink(`${jsonUploadPath}${e}`))))
res.json({status: 'success', response})
} catch (error) {
res.json({status: 'failed',error})
// console.log("tweets error", error.data.errors);
}
})
module.exports = router

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.

Async export of redis client in nodejs

The following code constructs a redis client and exports. I am fetching the redis password from vault secret management service and that call is a promise/async. The code doesnt wait for that call and it exports the redis client before async call completes. I am not sure what I am doing wrong here. Any idea?
import redis from 'redis';
import bluebird from 'bluebird';
import logger from '../logger';
import srvconf from '../srvconf';
import { getVaultSecret } from '../services/vault.service';
const vaultConfig = srvconf.get('vault');
bluebird.promisifyAll(redis);
let redisUrl = '';
const maskRedisUrl = (url) => url.replace(/password=.*/, 'password=*****');
const setRedisUrl = (host, port, pw) => {
const pwstring = pw ? `?password=${pw}` : '';
const url = `redis://${host}:${port}${pwstring}`;
console.log(`Setting redis_url to '${maskRedisUrl(url)}'`);
return url;
}
if (vaultConfig.use_vault) {
(async () => {
const secret = await getVaultSecret(`${vaultConfig.redis.secrets_path + vaultConfig.redis.key}`)
redisUrl = setRedisUrl(srvconf.get('redis_host'), srvconf.get('redis_port'), secret.PASSWORD);
})().catch(err => console.log(err));
} else {
if (!srvconf.get('redis_url')) {
redisUrl = setRedisUrl(srvconf.get('redis_host'), srvconf.get('redis_port'), srvconf.get('redis_password'));;
} else {
redisUrl = srvconf.get('redis_url');
console.log(`Found redis_url ${maskRedisUrl(redisUrl)}`);
}
}
const options = redisUrl
? { url: redisUrl }
: {};
const redisClient = redis.createClient(options);
redisClient.on('error', err => {
logger.error(err);
});
export default redisClient;
The problem is that (async () => {...})() returns a Promise and you are not awaiting it at the top-level, so the script continues to run past that line, sets options = {} and returns the redisClient.
What you need is a top-level await which is enabled by default in Node versions >= 14.8.0. However, if your project uses a version older than that, there is a workaround as shown below.
Please note that the below code is NOT tested since I do not have the same project setup locally.
Module
import redis from "redis";
import bluebird from "bluebird";
import logger from "../logger";
import srvconf from "../srvconf";
import { getVaultSecret } from "../services/vault.service";
const vaultConfig = srvconf.get("vault");
bluebird.promisifyAll(redis);
let redisUrl = "";
let redisClient = null;
const initRedisClient = () => {
const options = redisUrl ? { url: redisUrl } : {};
redisClient = redis.createClient(options);
redisClient.on("error", (err) => {
logger.error(err);
});
};
const maskRedisUrl = (url) => url.replace(/password=.*/, "password=*****");
const setRedisUrl = (host, port, pw) => {
const pwstring = pw ? `?password=${pw}` : "";
const url = `redis://${host}:${port}${pwstring}`;
console.log(`Setting redis_url to '${maskRedisUrl(url)}'`);
return url;
};
(async () => {
if (vaultConfig.use_vault) {
try {
const secret = await getVaultSecret(
`${vaultConfig.redis.secrets_path + vaultConfig.redis.key}`
);
redisUrl = setRedisUrl(
srvconf.get("redis_host"),
srvconf.get("redis_port"),
secret.PASSWORD
);
} catch (err) {
console.log(err);
}
} else {
if (!srvconf.get("redis_url")) {
redisUrl = setRedisUrl(
srvconf.get("redis_host"),
srvconf.get("redis_port"),
srvconf.get("redis_password")
);
} else {
redisUrl = srvconf.get("redis_url");
console.log(`Found redis_url ${maskRedisUrl(redisUrl)}`);
}
}
// Initialize Redis client after vault secrets are loaded
initRedisClient();
})();
export default redisClient;
Usage
At all places where you import and use the client, you always need to check if it is actually initialized successfully, and throw (and catch) a well defined error if it is not.
const redisClient = require("path/to/module");
...
if (redisClient) {
// Use it
} else {
throw new RedisClientNotInitializedError();
}
...

How can i write a mocha/chai test for a database connection along with queries?

I'm trying to test the dbMysqlConnect function in the file I that's being tested, but I'm having trouble actually testing the function for I always get an error when trying to establish a connection. How would you go about writing a test for a situation like this? Any help would be appreciated.
Error in console window:
Error: connect ECONNREFUSED 127.0.0.1:3306
Test File:
const mysql = require('mysql');
const mssql = require('mssql');
const actions = require('../src/actions');
const knowledgePortalAPI = require ('../src/api/knowledgePortalAPI');
const rewire = require('rewire');
const { expect } = require('chai');
const MockAdapter = require('axios-mock-adapter');
const Store = require('./store');
//import the updatedState and Event Variables from the store.
const newStore = new Store();
let updatedState = newStore.getUpdatedState();
let event = newStore.getEvent();
let initialState = newStore.getInitialState();
const _ = require('lodash');
describe('testing actions.js file', () => {
it('testing dbMysqlConnect function', async () => {
updatedState = _.cloneDeep(initialState);
const router = rewire('../src/actions');
const dbMysqlConnect = router.__get__('dbMysqlConnect');
let memoObject = await dbMysqlConnect(updatedState, event);
});
});
actions.js:
const mysql = require('mysql');
const mssql = require('mssql');
const axios = require('axios');
const AD = require('ad');
const bp = require('botpress');
const configMS = require('./configMS');
const configMY = require('./configMY');
const Querystring = require('querystring');
const flatten = require('flat');
const moment = require('moment');
const { viewClaimSummary, viewClaimLineDetail, viewClaimSummaryMulti, providerDemographics, claimConsolidated, memoService } = require('./api');
const realTimeFlowDialogEngine = require('./RealTimeFlowDialogEngine/documentDialogEngine');
const claimsDocumentAutoRoutingEngine = require('./RealTimeFlowDialogEngine/claimsSOPDocumentRouter');
const messageStressTest = require('./RealTimeFlowDialogEngine/messageStressTest');
/**
* Description of the action goes here
* #param {String} params.name=value Description of the parameter goes here
* #param {Number} [params.age] Optional parameter
*/
/**
* Demo call to MySQL DB
*/
async function dbMysqlConnect(state, event, params) {
var con = mysql.createConnection({
host: configMY.host,
user: configMY.user,
password: configMY.password,
database: configMY.database
})
con.connect(function (err) {
if (err) {
throw err;
}
con.query('select * from users', function (err, result, fields) {
if (err) {
throw err;
}
//console.log(result[0]);
event.reply('#builtin_text', {
text: `User name is : ${result[0].First_name}`,
typing: true
})
})
})
}
Since you're using rewire, you should be able to replace mysql with a mock implementation. I'd recommend sinon as a helpful tool for authoring that mock, though you don't have to do so.
const { stub } = require('sinon');
describe('testing actions.js file', () => {
it('testing dbMysqlConnect function', async () => {
updatedState = _.cloneDeep(initialState);
const router = rewire('../src/actions');
const mockUsers = [];
const mockConnection = {
connect: stub().yields(),
query: stub().yields(null, mockUsers)
};
const mockMySql = {
createConnection: stub().returns(mockConnection);
};
router.__set__('mysql', mockMySql);
const dbMysqlConnect = router.__get__('dbMysqlConnect');
let memoObject = await dbMysqlConnect(updatedState, event);
});
});

Problem with with ES2020 dynamic import()

I'm parsing all the modules in a directory, initialise them, and finally export them. The modules in question are knex models like this one:
// schema/users.js
import createModel from './common'
const name = 'User'
const tableName = 'users'
const selectableProps = ['userId', 'name', 'email', 'updated_at', 'created_at']
export default knex => {
const model = createModel({
knex,
name,
tableName,
selectableProps,
})
return { ...model }
}
Each of the model definitions is extended with common parts:
// schema/common.js
export default ({
knex = {},
name = 'name',
tableName = 'tablename',
selectableProps = [],
timeout = 1000,
}) => {
const findAll = () =>
knex
.select(selectableProps)
.from(tableName)
.timeout(timeout)
return {
name,
tableName,
selectableProps,
timeout,
findAll,
}
}
Finally, all models are initialised and exported:
// schema/index.js
import { readdir } from 'fs'
import { promisify } from 'util'
import { default as knex } from '../db'
let modules = {}
export default new Promise(async $export => {
const readFileAsync = promisify(readdir)
const getModels = async dir => await readFileAsync(dir)
const files = await getModels(__dirname)
for await (const file of files) {
if (file !== 'common.js' && file !== 'index.js') {
let mod = await import(__dirname + '/' + file).then(m => m.default(knex))
modules[mod.name] = mod
}
}
await $export(modules)
console.log(modules)
})
The above seem to be working but I'm not able to figure out how to import one of these modules from another file. I'm trying to do something along the lines of:
const User = async () => await import('../schema') // not working
or
const User = (async () => await import('../schema'))() // not working
Any help with this will be appreciated!

Categories

Resources