stop multiple service controller queries inside NodeJs promise while loop - javascript

I'm trying to start a windows service from a node script. This service has a bad habit of hanging and sometimes requires a retry to start successfully. I have a promise while loop setup (Please feel free to suggest a better way). The problem I'm having, is with each loop the sc.pollInterval output writes duplicate results in the console. Below is an example of the duplicate content I see in the console, this is after the second iteration in the loop, i'd like it to only display that content once.
sc \\abnf34873 start ColdFusion 10 Application Server
sc \\abnf34873 queryex ColdFusion 10 Application Server
SERVICE_NAME: ColdFusion 10 Application Server
TYPE : 10 WIN32_OWN_PROCESS
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x7d0
PID : 0
FLAGS :
SERVICE_NAME: ColdFusion 10 Application Server
TYPE : 10 WIN32_OWN_PROCESS
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x7d0
PID : 13772
FLAGS :
Here is the code I have. Basically, I'm going to try to start the service 3 times. If it doesn't, then I throw and error. One thing to note, when I attempt to start the service, but it's stuck in 'Start_pending' state, I kill the process and then try to start it again.
var retryCount = 0;
// Start the colfusion service
gulp.task('start-coldfusion-service', function(done) {
var serviceStarted = false;
console.log("Starting coldfusion service..");
// This says we're going to ask where it's at every 30 seconds until it's in the desired state.
sc.pollInterval(30);
sc.timeout(60);
retryCount = 0;
tryServiceStart().then(function(result) {
// process final result here
done();
}).catch(function(err) {
// process error here
});
});
function tryServiceStart() {
return startService().then(function(serviceStarted) {
if (serviceStarted == false) {
console.log("Retry Count: " + retryCount);
// Try again..
return tryServiceStart();
} else {
return result;
}
});
}
function startService() {
return new Promise(function(resolve, reject) {
var started = true;
// Make sure the coldfusion service exists on the target server
sc.query(targetServer, { name: 'ColdFusion 10 Application Server'}).done(function(services) {
// if the service exists and it is currentl stopped, then we're going to start it.
if (services.length == 1) {
var pid = services[0].pid;
if (services[0].state.name == 'STOPPED') {
sc.start(targetServer, 'ColdFusion 10 Application Server')
.catch(function(error) {
started = false;
console.log("Problem starting Coldfusion service! error message: " + error.message);
console.log("retrying...");
retryCount++;
if (parseInt(retryCount) > 2) {
throw Error(error.message);
}
})
.done(function(displayName) {
if (started) {
console.log('Coldfusion service started successfully!');
}
resolve(started);
});
} else if (services[0].state.name == 'START_PENDING') {
kill(pid, {force: true}).catch(function (err) {
console.log('Problem killing process..');
}).then(function() {
console.log('Killed hanging process..');
resolve(false);
});
}
} else {
console.log("Could not find the service in a stopped state.");
resolve(false);
}
});
});
}

Not too sure why you get duplicate results in the console, however below are some ideas on how the code might be better written, chiefly by promisifying at the lowest level.
Sticking fairly closely to the original concept, I ended up with this ...
Promisify sc commands
sc commands return something which is promise-like but with a .done() method that does not, in all probability, possess the full power of a genuine .then()
promisify each command as .xxxAsync()
by adopting each command's .done as .then, Promise.resolve() should be able to assimilate the promise-like thing returned by the command.
;(function() {
commands.forEach(command => {
sc[command].then = sc[command].done;
sc[command + 'Async'] = function() {
return Promise.resolve(sc[command](...arguments));
};
}).
}(['start', 'query'])); // add other commands as required
gulp.task()
promise chain follows its success path if service was opened, otherwise its error path
no need to test a result to detect error conditions in the success path.
gulp.task('start-coldfusion-service', function(done) {
console.log('Starting coldfusion service..');
// This says we're going to ask where it's at every 30 seconds until it's in the desired state.
sc.pollInterval(30);
sc.timeout(60);
tryServiceStart(2) // tryServiceStart(maxRetries)
.then(done) // success! The service was started.
.catch(function(err) {
// the only error to end up here should be 'Maximum tries reached'.
console.err(err);
// process error here if necessary
});
});
tryServiceStart()
orchestrate retries here
function tryServiceStart(maxRetries) {
return startService()
// .then(() => {}) // success! No action required here, just stay on the success path.
.catch((error) => {
// all throws from startService() end up here
console.error(error); // log intermediate/final error
if(--maxRetries > 0) {
return tryServiceStart();
} else {
throw new Error('Maximum tries reached');
}
});
}
startService()
form a fully capable promise chain by calling the promisified versions of sc.query() and sc.start()
console.log() purged in favour of throwing.
thrown errors will be caught and logged back in tryServiceStart()
function startService() {
// Make sure the coldfusion service exists on the target server
return sc.queryAsync(targetServer, { name: 'ColdFusion 10 Application Server'})
.then((services) => {
// if the service exists and it is currently stopped, then start it.
if (services.length == 1) {
switch(services[0].state.name) {
case 'STOPPED':
return sc.startAsync(targetServer, 'ColdFusion 10 Application Server')
.catch((error) => {
throw new Error("Problem starting Coldfusion service! error message: " + error.message);
});
break;
case 'START_PENDING':
return kill(services[0].pid, { 'force': true })
.then(() => {
throw new Error('Killed hanging process..'); // successful kill but still an error as far as startService() is concerned.
})
.catch((err) => {
throw new Error('Problem killing process..');
});
break;
default:
throw new Error("Service not in a stopped state.");
}
} else {
throw new Error('Could not find the service.');
}
});
}
Checked only for syntax error, so may well need debugging.
Offered FWIW. Feel free to adopt/raid as appropriate.

I have found another npm package called promise-retry that seems to have addressed the issue I was having. At the same time, I believe it made my code a little more clear as to what it's doing.
gulp.task('start-coldfusion-service', function(done) {
var serviceStarted = false;
console.log("Starting coldfusion service..");
// Since starting a service on another server isn't exactly fast, we have to poll the status of it.
// This says we're going to ask where it's at every 30 seconds until it's in the desired state.
sc.pollInterval(30);
sc.timeout(60);
promiseRetry({retries: 3}, function (retry, number) {
console.log('attempt number', number);
return startService()
.catch(function (err) {
console.log(err);
if (err.code === 'ETIMEDOUT') {
retry(err);
} else if (err === 'killedProcess') {
retry(err);
}
throw Error(err);
});
})
.then(function (value) {
done();
}, function (err) {
console.log("Unable to start the service after 3 tries!");
process.exit();
});
});
function startService() {
var errorMsg = "";
return new Promise(function(resolve, reject) {
var started = true;
// Make sure the coldfusion service exists on the target server
sc.query(targetServer, { name: 'ColdFusion 10 Application Server'}).done(function(services) {
// if the service exists and it is currentl stopped, then we're going to start it.
if (services.length == 1) {
var pid = services[0].pid;
if (services[0].state.name == 'STOPPED') {
sc.start(targetServer, 'ColdFusion 10 Application Server')
.catch(function(error) {
started = false;
errorMsg = error;
console.log("Problem starting Coldfusion service! error message: " + error.message);
console.log("retrying...");
})
.done(function(displayName) {
if (started) {
console.log('Coldfusion service started successfully!');
resolve(started);
} else {
reject(errorMsg);
}
});
} else if (services[0].state.name == 'START_PENDING') {
kill(pid, {force: true}).catch(function (err) {
console.log('Problem killing process..');
}).then(function() {
console.log('Killed hanging process..');
reject("killedProcess");
});
} else {
// Must already be started..
resolve(true);
}
} else {
console.log("Could not find the service in a stopped state.");
resolve(false);
}
});
});
}

Related

Why does this javascript code not redirect the user after the metamask transaction?

I want to make a javascript code that does a metamask transaction and redirects the user to another page after the transation is completed. How easy this may sound, I can not figure it out.
My current code lets the user complete the transaction, but it does not redirect the user to another page. Instead, it gives this error: "MetaMask - RPC Error: invalid argument 0: json: cannot unmarshal non-string into Go value of type common.Hash"
I have looked it up, but I could not find any possible fix for my problem.
This is my code:
try {
// send the transaction
ethereum.send({
method: 'eth_sendTransaction',
params: [
{
to: contractAddress,
from: userAddress,
value: amt
},
],
}, (error, transactionHash) => {
if (error) {
console.error(error);
} else {
// check the status of the transaction using the transaction hash
ethereum.request({
method: 'eth_getTransactionReceipt',
params: [transactionHash],
}).then((receipt) => {
// check if the transaction was successful
if (receipt.status === '0x1') {
console.log(`Transaction was successful`);
// redirect to another page
window.location.href = "page.php";
} else {
console.log(`Transaction failed`);
}
}).catch((error) => {
// This is the line of code the error is assigned to:
console.error(error);
});
}
});
} catch (error) {
console.error(error);
}
});
} else {
document.getElementById("bericht").innerHTML = "Install Metamask before you continue";
return;
}
I have tried looking the error up on internet, but nothing significant showed up. Could anyone help? Thank you in advance!

socket in nodejs will not close no matter what

So I'm trying to make a script that will connect a bot to a kahoot. However I can only use a certain name one time otherwise I will get a duplicate name error, even after closing all sockets, terminating sockets, removing listeners, etc. Is there something I'm missing?
Edit: added the cleanup on process end that still doesn't work
var Kahoot = require("kahoot.js-latest");
var colors = require('colors');
var bot = new Kahoot;
function cleanup() {
bot.leave();
bot.removeAllListeners();
bot.socket.close();
bot.socket.terminate();
}
bot.join(process.argv[2], "a")
.then(() => {
console.log("Joining...".cyan);
})
.catch((err) => {
console.log(err);
cleanup();
});
[`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `SIGTERM`].forEach((eventType) => {
process.on(eventType, (code) => {
for (n in bots) {
cleanup();
}
console.log("Exiting Process with code: ".yellow + code);
})
})
After terminating the process and trying to run it again with the same name, I get a duplicate name error even though surely that connection should be destroyed by now
Edit: the error:
{
description: 'Duplicate name',
type: 'loginResponse',
error: 'USER_INPUT'
}

Firebase how to break realtime database transaction with different state/message if different condition is true?

is it a good practice at all and if yes what is the correct way to break a transaction with different error states/messages for different situations?
I have a transaction running over an 'offer' entry doing 'seats' reservation:
I want to break it if one of the following 3 conditions is true and return state/message to the caller function.
if the asking user already made a reservation of seats of this offer.
if there is not enought seats.
if this offer does not exists.
And if everything is ok the transaction should complete normally and return state/message to the caller function that reservation is made.
I'm not sure how to break the transaction in case of one of the conditions is true.
if I use throw new Error('description of the problem.') then this will be an Exception and it's not handled by catch() handler of the transaction Promise
and I'm not sure how to handle this exception because it's an asynchronous function here.
so I think i should not use an exception.
Here is what I mean:
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
**? how to break the transaction with state/message 'You already have a deal.' ? and how to handle it below ?**
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
} else {
**? how to break the transaction with state/message 'Not enought seats.' ? and how to handle it below ?**
}
}
return entry;
**? how to check if 'entry' is really null ? i.e. offer does not exists ?** and break and handle it.
})
.then((success)=>{
return success.snapshot.val();
})
.catch((error)=>{
return Promise.reject(error);
});
}
here is my data in realtime database:
activeOffers
-LKohyZ58cnzn0vCnt9p
details
direction: "city"
seatsCount: 2
timeToGo: 5
uid: "-ABSIFJ0vCnt9p8387a" ---- offering user
here is my test data sent by Postman:
{
"data":
{
"uid": "-FGKKSDFGK12387sddd", ---- the requesting/asking user
"id": "-LKpCACQlL25XTWJ0OV_",
"details":
{
"direction": "city",
"seatsCount": 1,
"timeToGo": 5
}
}
}
==== updated with final source ====
many thanks to Renaud Tarnec!
So here is my final source that is working fine. If someone sees a potential problem please let me know. Thanks.
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
var abortReason;
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
abortReason = 'You already made a reservation';
return; // abort transaction
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
// Reservation is made
} else {
abortReason = 'Not enought seats';
return; // abort transaction
}
}
return entry;
})
.then((result)=>{ // resolved
if (!result.committed) { // aborted
return abortReason;
} else {
let value = result.snapshot.val();
if (value) {
return value;
} else {
return 'Offer does not exists';
}
}
})
.catch((reason)=>{ // rejected
return Promise.reject(reason);
});
}
The only pain is a warning during deploy in VSCode terminal about this abortions by returning no value:
warning Arrow function expected no return value consistent-return
currently I'm not sure if I could do anything about it.
Look at this doc in the Firebase API Reference documentation: https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction
Below is the code from this doc. Look how return; is used to abort the transaction (The doc also says: "you abort the transaction by not returning a value from your update function"). And note how this specific case is handled in the onComplete() callback function that is called when the transaction completes (within else if (!committed){} ).
// Try to create a user for ada, but only if the user id 'ada' isn't
// already taken
var adaRef = firebase.database().ref('users/ada');
adaRef.transaction(function(currentData) {
if (currentData === null) {
return { name: { first: 'Ada', last: 'Lovelace' } };
} else {
console.log('User ada already exists.');
return; // Abort the transaction.
}
}, function(error, committed, snapshot) {
if (error) {
console.log('Transaction failed abnormally!', error);
} else if (!committed) {
console.log('We aborted the transaction (because ada already exists).');
} else {
console.log('User ada added!');
}
console.log("Ada's data: ", snapshot.val());
});
So IMHO you should adopt the same pattern and at the places in your code where you ask "**? how to break the transaction" you do return;.
Update: You can differentiate the abortion cases by using a variable, as follows. If you add, via the Firebase console, a node age with value > 20 to users.ada.name, the first abortion cause will be "triggered".
var adaRef = firebase.database().ref('users/ada');
var transactionAbortionCause; //new variable
adaRef.transaction(function(currentData) {
if (currentData === null) {
return { name: { first: 'Ada', last: 'Lovelace' } };
} else if (currentData.name.age > 20) {
transactionAbortionCause = 'User ada is older than 20'; //update the variable
console.log('User ada is older than 20');
return; // Abort the transaction.
} else {
transactionAbortionCause = 'User ada already exists'; //update the variable
console.log('User ada already exists');
return; // Abort the transaction.
}
}, function(error, committed, snapshot) {
if (error) {
console.log('Transaction failed abnormally!', error);
} else if (!committed) {
console.log('We aborted the transaction because ' + transactionAbortionCause); //use the variable
} else {
console.log('User ada added!');
}
console.log("Ada's data: ", snapshot.val());
});
If I am not mistaking, you could also do that with promises, as you do in your code. The doc says that the transaction returns a non-null firebase.Promise containing {committed: boolean, snapshot: nullable firebase.database.DataSnapshot} and explains that this promise "can optionally be used instead of the onComplete callback to handle success and failure".
So by:
Doing return; for your two cases of abortion, and
Reading the value of the committed boolean
you should be able to handle the abortion cases in your code by doing
.then((result)=>{
if (result.commited) {... } else { /*abortion!*/}
})
I have not tested this approach however

Protractor Javascript not executing in sequence

I have the below function for login, which would console "successful" on success and "Failed" for files ones.
function login() {
return new Promise(function (resolve, reject) {
var username = element(by.name('txtLogin'));
var password = element(by.id('txtPassword'));
var signInButton = element(by.id('btnSignIn'));
for (var i = 0; i < testData.length; i++) {
if (testData[i].env === appConfig) {
username.sendKeys(testData[i].user);
password.sendKeys(testData[i].pass);
signInButton.click();
console.log("Successfully Clicked on the Sign In Button!");
break;
}
}
browser.getTitle().then(function (title) {
if (title == "Page Title") {
resolve("Successfull");
} else {
reject("Failed");
}
});
});
};
And the following test
describe('Login Scenarios', function () {
beforeEach(function () {
login();
});
it('Valid Credentials, Should login successfully from util class', function () {
console.log('Successfully logged in!');
});
});
I am seeing very strange behavior here. This line executes and consoles output even before the page is fully loaded.
console.log("Successfully Clicked on the Sign In Button!");
and the below code never gets executed.
browser.getTitle().then(function (title) {
if (title == "Page Title") {
resolve("Successfull");
} else {
reject("Failed");
}
});
And in the end i see the following error.
failed: error while waiting for protractor to sync with the page: "cannot re ad property '$$testability' of undefined"
I am pretty sure, i have messed up something here. But unable to figure out what's wrong that am doing here.
login returns a promise, but you're not signalling to Jasmine/Protractor that it needs to wait for it to finish. You can do this using the done callback:
beforeEach(function (done) {
login().then(function () {
done();
});
});
See the Jasmine documentation (which seems to have been inexplicibly hidden away in their new website layout...) for more info.

Promise either never get called, or is rejected (Parse JS SDK)

I am trying to write a function that add or edit some fields on a User object.
The problem come when I try to save the user, if I use user.save, the Promise is rejected with error 206 UserCannotBeAlteredWithoutSessionError.
However, if I get the session id (and documentation about that is scarce), the promise never get resolve, nor rejected. The app seems to just jump to the callback.
My function:
function update(user, callback) {
let query = new Parse.Query(Parse.User);
query.equalTo("username", user.email);
query.find().then(
(users) => {
if(users.length === 0) {
callback('Non existent user');
} else {
let user = users[0];
// user.set('some', 'thing');
console.log('save');
user.save(/*{
sessionToken: user.getSessionToken()
}*/).then(
(test) => {
console.log('OK - ' + test);
callback();
}, (err) => {
console.log('ERR- ' + require('util').inspect(err));
// console.log(callback.toString());
callback(error.message);
}
);
}
},
(error) => {
callback(error.message);
}
);
}
Called with:
var async = require('async'),
baas = require('./baas.js');
async.waterfall([
(callback) => {
callback(null, {
email: 'user#test.com',
password: 'password'
});
},
(user, callback) => {
console.log('connect');
baas.connect(() => { //Initialize the connection to Parse, and declare use of masterKey
callback(null, user);
});
},
(user, callback) => {
console.log('update');
baas.update(user, (err) => {
callback(err);
});
}
], (err) => {
console.log('Error: ' + err);
});
The logs become:
Without session token:
connect
update
save
ERR- ParseError { code: 206, message: 'cannot modify user sA20iPbC1i' }
With session token:
connect
update
save
I do not understand how it is possible that the promise just callback without printing anything, nor why no error are raised anywhere.
Edit:
Following #user866762 advice, I tried to replace the query with Parse.User.logIn and use the resulting User object.
While this solution give me a sessionToken, the end result is the same, parse crash if I don t provide the session token, or give me a error if I do.
According to the Parse Dev guide:
...you are not able to invoke any of the save or delete methods unless the Parse.User was obtained using an authenticated method, like logIn or signUp.
You might also try becoming the user before saving, but I have my doubts that will work.
When you're "get[ting] the session id" my guess is that you're really breaking something. Either Parse is having a heart attack at you asking for the session token, or when you're passing it in save you're causing something there to explode.

Categories

Resources