I try to use couchbase.Mock in my unit test with the couchbase nodejs SDK. But i have this error :
createPrimaryIndex is not a function
with this code:
cluster = new couchbase.Cluster(options.protocol + "://" + options.server + ":" + options.port);
open().then(() => createPrimaryIndex());
function open() {
return new Promise((resolve, reject) => {
bucket = cluster.openBucket(options.bucketName, (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
function createPrimaryIndex() {
return new Promise((resolve, reject) => {
bucket.manager().createPrimaryIndex({
ignoreIfExists: true
}, (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
It work perfectly when i use couchbase but it fail if i use couchbase.Mock
Not all functions are implemented by the mock.
See this forum post regarding a similar issue and a workaround using sinon.js:
Node.js Mock N1QL Query Support
Related
I'm currently a little confused as to how to properly wait for the promise to finish before returning the result from a query
Here is my current code:
const getLeaderboardValues = async () => {
const SQLConnection = await getSQLConnection();
return new Promise((resolve, reject) => {
SQLConnection.query(getValuesSQLQuery, (err, result) => {
if (err) { reject(err) }
return resolve(result);
});
SQLConnection.end()
})
}
const runtime = () => {
getLeaderboardValues().then((result) => {
console.log(result);
})
}
The code above does log the correct results while debbugging, i believe this is because i'm giving the code more time to render with the breakpoints, however when running normally i get undefined
I believe the SQLConnection.end() line is executing before the query is returned, given it is outside the query statement.
The below may solve your issue, however I do not recommend opening a connection and closing it on every request in production systems.
const getLeaderboardValues = async () => {
const SQLConnection = await getSQLConnection();
return new Promise((resolve, reject) => {
SQLConnection.query(getValuesSQLQuery, (err, result) => {
if (err) {
reject(err)
return SQLConnection.end()
}
SQLConnection.end()
return resolve(result);
});
})
}
const runtime = () => {
getLeaderboardValues().then((result) => {
console.log(result);
})
}
Can't I use promise for nodeJS mysql query?
// My DB settings
const db = require('../util/database');
db.query(sqlQuery, [param1, param2])
.then(result => {
console.log(result);
})
.catch(err => {
throw err;
});
It is returning: TypeError: db.query(...).then is not a function
You mentioned in the comments that you want logic after the query block to be awaited, without placing that logic inside of the callback. By wrapping the method with a Promise, you can do that as such:
try {
const result = await new Promise((resolve, reject) => {
db.query(sqlQuery, (error, results, fields) => {
if (error) return reject(error);
return resolve(results);
});
});
//do stuff with result
} catch (err) {
//query threw an error
}
Something like this should work
function runQuery(sqlQuery){
return new Promise(function (resolve, reject) {
db.query(sqlQuery, function(error, results, fields) {
if (error) reject(error);
else resolve(results);
});
});
}
// test
runQuery(sqlQuery)
.then(function(results) {
console.log(results)
})
.catch(function(error) {
throw error;
});
mysql package does not support promise. We can use then only a function call returns a promise.You can use mysql2 which has inbuilt support for Promise. It will also make your code more readable. From mysql2 docs:
async function main() {
// get the client
const mysql = require('mysql2/promise');
// create the connection
const connection = await mysql.createConnection({host:'localhost',
user: 'root', database: 'test'});
// query database
const [rows, fields] = await connection.execute(query);
// rows hold the result
}
I would aslo recommend you to learn about callbacks, promise and async-await
Before I commit anything to the database, I want all my update promises resolve; otherwise, I rollback. In other words, I want atomicity. I suppose I could handle the rollback by deleting out rows, but this has its own risks. I noticed if there is an error in any of the promises, the data still gets updated in database. What am I doing wrong?
I have written a simple program to illustrate the issue.
This is the main process:
const db = require('./db.js');
const async = require('async');
let insertList = [];
for (let i = 0; i<3; i++) {
insertList.push(i);
}
async function func1 () {
return new Promise((resolve, reject) => {
console.log("In Func1");
async.forEachOf(insertList, function(value, key, callback) {
console.log('>>>>' + value + '<<<<<<' + key );
db.insertOne('coll1', {value}).then(() => {
callback();
}).catch(err => {callback(err)})
}, function(err) {
// if any of the file processing produced an error, err would equal that error
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('err:', err);
reject(err);
} else {
console.log('Col1 All inserts have been processed successfully');
resolve("Success");
}
});
})
}
function func2 () {
return new Promise((resolve, reject) => {
console.log("In Func2");
async.forEachOf(insertList, function(value, key, callback) {
console.log('>>>>' + value + '<<<<<<' + key );
db.insertOne('coll2', {value}).then(() => {
callback();
}).catch(err => {callback(err)})
}, function(err) {
// if any of the file processing produced an error, err would equal that error
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('err:', err);
reject(err);
} else {
console.log('Col2 All inserts have been processed successfully');
resolve("Success");
}
});
})
}
function func3 () {
return new Promise((resolve, reject) => {
console.log("In Func3");
async.forEachOf(insertList, function(value, key, callback) {
console.log('>>>>' + value + '<<<<<<' + key );
if(key === 1) {
value = 'a';
}
db.insertOne('coll3', {value}).then(() => {
callback();
}).catch(err => {callback(err)})
}, function(err) {
// if any of the file processing produced an error, err would equal that error
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('err:', err);
reject(err);
} else {
console.log('Col3 All inserts have been processed successfully');
resolve("Success");
}
});
})
}
db.connect().then((pool) => {
pool.getConnection((err, connection) =>{
if (err)
return console.error(err);
else {
}
connection.beginTransaction((err) => {
if (err) {
return console.error(err);
}
let func1Promise = new Promise((resolve, reject) => {func1().then(() => {
console.log("Func1 complete");
resolve("Func1 complete");
}).catch((err) => {
console.error("Func1 ERROR: ", err);
reject("Func1 ERROR: ", err);
})});
let func2Promise = new Promise((resolve, reject) => {func2().then(() => {
console.log("Func2 complete");
resolve("Func2 complete");
}).catch((err) => {
console.error("Func2 ERROR: ", err);
reject("Func2 ERROR: ", err);
})});
let func3Promise = new Promise((resolve, reject) => {func3().then(() => {
console.log("Func3 complete");
resolve("Func3 complete");
}).catch((err) => {
console.error("Func3 ERROR: ", err);
reject("Func3 ERROR: ", err);
})});
Promise.all([func1Promise, func2Promise, func3Promise])
.then(()=> {
console.log("All Processes completed successfully.");
connection.commit(err => {
if (err) {
connection.rollback(() => {
throw err;
});
}
console.log('Commit Complete.');
connection.release();
});
})
.catch((err)=> {
console.error(err);
console.error("An update process has failed.");
connection.rollback(() => {
console.error(err);
connection.release();
});
})
});
})
});
The db.js looks like this:
const mysql = require('mysql');
const config = {
db: {
host: 'localhost',
user: process.env.DBUSER,
password: process.env.DBPASSWORD,
database: 'test',
}
};
var pool;
class DB {
constructor(host, user, password, database) {
this.host = host;
this.user = user;
this.password = password;
this.database = database;
}
connect() {
return new Promise((resolve, reject) => {
pool = mysql.createPool({
connectionLimit: 10,
host : this.host,
user : this.user,
password : this.password,
database : this.database
});
resolve(pool);
});
}
objToArray(obj) {
let arr = obj instanceof Array;
return (arr ? obj : Object.keys(obj)).map((i) => {
let val = arr ? i : obj[i];
if(typeof val === 'object' && val !== null)
return this.objToArray(val);
else
return val;
});
}
insertOne(collection, insertObj) {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) {
resolve(err);
} else {
let sql = "INSERT INTO " + collection + " VALUES (?)";
// Convert the array of objects into an array of arrays.
let responseJson = this.objToArray(insertObj);
// The query object expects an array of objects so you pass in 'responseJson' as is
console.log(responseJson);
connection.query(sql, [responseJson], (err, result) => {
if (err) {
console.error(err);
return reject(err);
}
//console.log(result);
resolve("SUCCESS: object inserted into database");
});
}
});
});
}
}
const db = new DB(config.db.host, config.db.user, config.db.password, config.db.database);
Object.freeze(db);
module.exports = db;
My database "test" is simple and consists of 3 tables, coll1, coll2, coll3 and each has on field which is type int. In the third function I replace the 1 with 'a' This causes an error and the code catches this error and attempts a rollback, which does not work. If I set a breakpoint after func1 is executed and check the database, the values are already in the database.
Here is the version of MySQL that I am running:
Variable_name,Value
innodb_version,8.0.11
protocol_version,10
slave_type_conversions,
tls_version,"TLSv1,TLSv1.1,TLSv1.2"
version,8.0.11
version_comment,"MySQL Community Server - GPL"
version_compile_machine,x86_64
version_compile_os,macos10.13
version_compile_zlib,1.2.11
I am using the following NPM packages in node:
"async": "^2.6.2",
"mysql": "^2.15.0"
You're creating a transaction on a connection created in your test program, but your db.js's insertOne is grabbing a new connection from the pool that does not have a transaction. You should be passing in the connection you created in the test program.
I have a problem with creating API Key associated with an Usage Plan in AWS API Getaway (using AWS SDK for node.js).
In AWS Console you can attach API Key to Usage Plan via this button:
However I could not find a similar function in AWS SDK documentation
This code do the magic:
var params = {
keyId: 'STRING_VALUE', /* required */
keyType: 'STRING_VALUE', /* required */
usagePlanId: 'STRING_VALUE' /* required */
};
apigateway.createUsagePlanKey(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
What I was missing was that keyType must be "API_KEY"
This is a full example of creating an API Key and assigning it to a usage plan.
async function createApiKey(apikey_name, usage_plan="free") {
const usage_plans = {
free: "kotc0f", // usage plan IDs
basic: "ju5fea"
};
if (!usage_plan || !(usage_plan in usage_plans)) {
console.log(usage_plan + " usage plan does not exist");
return false;
}
var params = {
name: apikey_name,
description: "Created via API on " + (new Date).toISOString(),
enabled: true,
generateDistinctId: true
};
let api_key = await new Promise((resolve) => {
apigateway.createApiKey(params, function (err, data) {
if (err) {
console.log("ERROR", err, err.stack); // an error occurred
resolve(false);
}
else {
resolve(data);
}
});
});
if (!api_key) return false;
params = {
keyId: api_key.id,
keyType: "API_KEY",
usagePlanId: usage_plans[usage_plan]
};
await new Promise((resolve) => {
apigateway.createUsagePlanKey(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
resolve(false);
}
else {
resolve(data);
}
});
});
return api_key;
}
I have a scenario where the webservice needs to check for existense of key in redis if present give it as a response else get it from mysql, store in redis and then give it as response.
So i am using promises concept where first time when i call return new Set_Data(); it doesn't go to next then block it just stays idle. But next time since data already exists the return new Set_Data();
is not executed which is correct.
But why is that i am getting problem for first time when i call return new Set_Data(); which is not going for next then block.
Below is my code
constants.js file
var Promise = require('bluebird');
module.exports =
{
getRedisConnection: function ()
{
return require("redis").createClient(6379, 'path', { auth_pass: 'key' });
},
getMySqlConnection: function ()
{
var conObj = { host: "localhost", user: "root", password: "", database: "deccan" };
var connection = require("mysql").createConnection(conObj);
return new Promise(function (resolve, reject)
{
connection.connect(function (error)
{
if (error)
reject(error);
else
resolve(connection);
});
});
}
};
webservicefile.js
var Promise = require('bluebird');
var express = require('express');
var router = express.Router();
var constants = require("../constants");
function getSettings(request, response)
{
var client = constants.getRedisConnection();
get_Data();
function get_Data()
{
return new Promise(function (resolve, reject)
{
client.get("url", function (error, reply)
{
if (error)
reject(error);
else
{
if (reply == null)
resolve(); // Key not present so create
else
resolve(reply);
}
});
}).
catch(function (e)
{
console.log("Error at : " + new Date().toString() + ", => " + e);
}).
then(function (urlResult)
{
return new Promise(function (resolve, reject)
{
if (urlResult == undefined || urlResult == null)
{
return new Set_Data();
}
else
{
client.quit();
return resolve(urlResult);
}
});
}).
then(function (urlResult)
{
if (urlResult)
response.status(200).send({ url : urlResult });
else
response.status(500).send();
})
}
function Set_Data()
{
constants.getMySqlConnection().then(function (connection)
{
return new Promise(function (resolve, reject)
{
connection.query("select url from table where id = 1", function (error, results)
{
connection.end();
if (error)
reject(error);
else
resolve(results);
});
});
}).
then(function (url)
{
return new Promise(function (resolve, reject)
{
client.set('url', url, function (err, reply)
{
if (err)
reject(err);
else
resolve(url);
});
});
});
}
}
A couple of changes should do the trick, first Set_Data() doesn't return a promise like you think it does, add a return:
function Set_Data() {
return constants.getMySqlConnection().then(function (connection).then()
// ...
}
Inside this callback, you don't have a resolve() in the if so the promise is never resolved, returning something doesn't resolve:
// your code
then(function (urlResult) {
return new Promise(function (resolve, reject) {
if (urlResult == undefined || urlResult == null) {
return new Set_Data();
} else {
client.quit();
return resolve(urlResult);
}
});
}).
Return Set_Data() which is now a promise or the url:
then(function (urlResult) {
if (urlResult == undefined || urlResult == null) {
return new Set_Data();
} else {
client.quit();
return urlResult;
}
}).
On a side note, don't format your js code like C#, { shouldn't be on a new line.
You can return new Set_Data() directly if no urlResult, or return urlResult otherwise - inside then any (non Promise) value returned is a resolved Promise - so the .then chain will continue as required
function get_Data() {
// ...
.then(function (urlResult) {
// you don't need a "new Promise" here
if (urlResult == undefined || urlResult == null) {
return new Set_Data();
} else {
client.quit();
return urlResult;
}
})
// ...
}
One thing I noticed in your code is a .catch in the middle of your .then chain which will effectively turn an error into a fulfilled promise - not sure if that's the behaviour you are looking for. Something else to look out for