Protractor: "Cannot read property 'then' of undefined" - request

I am using Node's 'request' module to make 2 http requests for a unit test. I have to make sure 1 is carried out before the other and am trying to do so using a promise, but am getting the error "Cannot read property 'then' of undefined". Below is the function where the error is occuring:
this.setupSpec = function () {
var deleteItemsRepo = deleteRepo(strings.editRepo);
deleteItemsRepo.then(function () {
createRepo(strings.editRepo);
}); //todo: this must complete before createRepo starts, npm install async?
//createRepo(strings.editRepo);
return deleteItemsRepo; //return promise
};
deleteRepo(...) and createRepo(...) are just functions that call http requests.
Here is deleteRepo():
var deleteRepo = function (repoName) {
return request.delete(browser.params.baseRestUrl + 'repositories/' + repoName,
{'auth': browser.params.auth}, function (e, r, user) {
console.log("Status code of deleteRepo('" + repoName + "'): " + r.statusCode);
});
};

deleteRepo would return a Promise only if request.delete returns a Promise - which seems unlikely considering it uses a callback
"promisify" deleteRepo as follows for fun and profit
var deleteRepo = function(repoName) {
return new Promise(function(resolve, reject) {
request.delete(browser.params.baseRestUrl + 'repositories/' + repoName, {'auth': browser.params.auth}, function(e, r, user) {
console.log("Status code of deleteRepo('" + repoName + "'): " + r.statusCode);
if(e) {
return reject(e);
}
resolve(r.statusCode);
});
});
};

Related

deferred promise not working

I am using promise in angular for my web app like this-
var deferred = $q.defer();
On Success -
deferred.resolve(profile); // profile = JSON object
On Failure -
deferred.reject(1); // 1 or no value returned
At end of function -
return deferred.promise;
Then I am handing for this returned Promise Object to call another method.But it doesn't call. While if i use Callback(error,success) it works fine.Can somebody suggest what is wrong with my promise.
Code Snippet-
function open() { // for initializing DB,getting called from service
var deferred = $q.defer();
var options = {
Encryption: {
encryptKey: false, // optional encrypt primary key
secrets: [{
name: 'dddd',
key: 'xxxxxxxxxx'
}]
}
};
var schema = {
stores:[{
name:'profile',
encrypted: true
}]
};
var db = new ydn.db.Storage('nowconferdb', schema, options);
db.onReady(function() {
console.log('DB is initialized'); // getting this
profilestorage.setDB(db); // getting called and setting DB in profilestorage service
deferred.resolve(true);
});
db.addEventListener('fail', function (event) {
var err = event.getError();
if (err.name == 'versionchange') {
console.log('The application is updated, please refresh to upgrade.');
profilestorage.setup(db);
} else {
console.log('connection failed with ' + err.name + ' by ' + err.message);
db = null; // no operation can be placed to the database instance
}
deferred.reject(false);
});
return deferred.promise;
}
This is my calling method -
storageservice.open().then(function() {
console.log('post initializing storageservice'); // not getting it.
});
Thanks a lot for your sincere efforts.
What you should do is to call your function inside the success callback of the promise. Assumming you assign your promise to deferred variable:
deferred.then(
function (data) {
// Here you call your other function/method
// It will be called when 'deferred' promise is resolved
anotherFunction();
},
function (error) {
// Handle the error
}
);
I hope this helps, even though the information you give is not enough.

Angular callback or promise to trap return vale

I am racking my brain trying to get this to work correctly. I have tried callbacks and promises and am open to both. I modeled my code using the Mean Stack as laid out by Joe Eames in a Pluralsight class. I am still learning so please excuse my terminology if incorrect but here is how this works with code as illustrated below.
My interface calls flQuestionCrudCtrl.createQuestion that calls flQuestionCrud.createNewQuestion that calls flQuestion.Create. flQuestion.Create makes an http post request to a server that writes to a mongodb collection. Everything seems to work fine, data is written to the database and I can see the return value in flQuestionCrud per console logs. When I try to read the “data” return parameter in flQuestionCrudCtrl I get, “Error: data is undefined”. I have tried every combination of callback and promise but cannot get it to work. Can you see what I am doing wrong?
angular.module('app').controller( 'flQuestionCrudCtrl',function(flConstructDataService, flQuestionDataService, flQuestionCrud, flCachedReferenceData,flCachedQuestions, flAnswer, $scope, $location, $q, flIdentity, flNotifier,$timeout, $state) {
$scope.createQuestion = function() {
function doWorkAsync() {
return $timeout(flQuestionCrud.createNewQuestion(currentQuestionData), 10);
}
doWorkAsync()
.then(function(data) {
console.log("flQuestionCrudCtrl - Success " + data);
console.log("flQuestionCrudCtrl - Success Statement " + data.statement);
console.log("flQuestionCrudCtrl - Success Question id " + data._id);
currentQuestionData._id = data._id;
flQuestionDataService.setNewQuestionId(data._id);
console.log("flQuestionCrudCtrl - currentQuestionData._id " + currentQuestionData._id);
console.log("flQuestionCrudCtrl - flQuestionDataService.getNewQuestionId() " + flQuestionDataService.getNewQuestionId());
$state.go('questionUpdate');
})
.catch(function(err) {
console.log("flQuestionCrudCtrl - Error " + err);
$state.go('questionCreate');
})
.finally();
};
}
angular.module('app').factory('flQuestionCrud',function($http, $q, $state, $timeout, flQuestion ){
return {
createNewQuestion: function(newQuestionData) {
console.log("Before - flQuestion.create");
function createQuestionDataAsync(questionData) {
console.log("flQuestionCrud - Before Call to create ")
var returnData;
flQuestion.create(questionData, function(data) {
console.log("flQuestionCrud - After Call to create ")
if (!data){
return Error("Error Creating Data");
//return null;
}
else {
console.log("flQuestionCrud - Try Section - Success " + data);
console.log("flQuestionCrud - Try Section - Success Statement " + data.statement);
console.log("flQuestionCrud - Try Section - Success Question id " + data._id);
}
return data;
});
}
createQuestionDataAsync(newQuestionData);
},
Your flQuestionCrud.createNewQuestion() method should return the promise resolved with data:
angular.module('app')
.factory('flQuestionCrud', function($http, $q, $state, $timeout, flQuestion ){
return {
createNewQuestion: function(newQuestionData) {
var deferred = $q.defer();
flQuestion.create(questionData, function(data) {
if (data){
// whatever we resolve the promise with will be passed
// to the `then` handler in the controller
deferred.resolve(data);
}
else {
deferred.reject("Error Creating Data");
}
});
return deferred.promise;
}
// ...
}
});
Note, we're explicitly creating the promise and resolve or reject it depending on what flQuestion.create() returns.
This way you'll be able to access the data in your controller the way you do it:
flQuestionCrud.createNewQuestion(currentQuestionData)
.then(function(data) {
// work with data here
});
or
doWorkAsync()
.then(function(data) {
// work with data here
});

Not returning Restangular back-end call correctly

My back-end call is returning undefined. A.k.a TypeError: Cannot read property 'then' of undefined. I think I am calling it incorrectly.
Here is the AngularJS controller code:
$scope.addUser = function (chaseUser) {
Accounts.addChaseUser(userToSubmit).then(function (response, err) {
if (err) {
$scope.errorMessage = "There was an error.";
$log.debug(err);
} else if (response) {
$scope.errorMessage = "It worked.";
$log.debug(response);
} else {
$scope.errorMessage = "No 'response' nor 'err' returned from backend";
}
});
};
How this responds is that if...
(1) I put in correct credentials, I get a response that comes back with all the transaction data but still TypeError: Cannot read property 'then' of undefined in the console.
(2) Input incorrect credentials, I get no error object, response object, or even making it down to the line where I have $scope.errorMessage = "No 'response' nor 'err' returned from backend"; plus, of course, `cannot read property 'then' of undefined.
Corresponding AngularJS service:
return {
addChaseUser: function(credentials) {
return Restangular.one('user').customPOST(credentials, 'addUser');
}
};
On the backend (controller):
module.exports = {
addChaseUser: function (req, res) {
PlaidService.provideCredentialsToMFA(req.body, function (err, mfaRes) {
if (err) {
return res.status(403).json(err);
}
return res.json(mfaRes);
});
},
};
Backend service:
var plaid = require('plaid');
var plaidClient = new plaid.Client('test_id', 'test_secret', plaid.environments.tartan);
module.exports = {
provideCredentialsToMFA: function (credentials, cb) {
Q.fcall(PlaidService.connectUser.bind(this, credentials))
.then(PlaidService.saveUsersAccessToken.bind(this, credentials))
.then(PlaidService.getTransactionData.bind(this, credentials))
.then(function(transactions) {
cb(null, transactions);
},
function(err) {
console.log(err);
cb(err, null);
});
},
}
How am I supposed to be calling this Restangular POST from the AngularJS controller? It should not be returning undefined.
Since you are getting the response from the server side, it means that your server side code and the angular service code is working just fine. :)
The only possibility I can see is that I is wrong with the application of .then() block is that insted of two parameters(i.e., response and err) lets try with only one parameter.
Something like following :
$scope.addUser = function (chaseUser) {
Accounts.addChaseUser(userToSubmit).then(function (response) {
if(response.status == 'error'){
$log.debug('Got error in the response');
}else{
$log.debug('SUCCESS');
}
});
};
I have used .then(function(data)) with only one parameter. That is the only thing I could find :)
Incase you still don't see it, you are missing a few returns in your functions.
addUser: function (req, res) {
return PlaidService.provideCredentialsToMFA(
// ... your code ...
);
},
provideCredentialsToMFA should return the promise
provideCredentialsToMFA: function (credentials, cb) {
return plaidClient.addConnectUser(
// ... Your code ...
);
}
EDIT:
simplifying the working code looks something like this. Notice there are no returns and only callbacks are used:
findChargeById: function (req, res) {
fetchCharge(someParams, someCallBack);
}
fetchCharge: function (chargeId, cb) {
stripe.charges.retrieve(chargeId, anotherCallBack);
}
Your code looks like this. It's similar but the $scope.addUser expects something returned and doesn't use a callback
addUser: function (req, res) {
provideCredentialsToMFA(someParams, someCallBack);
// addUser returns nothing
}
provideCredentialsToMFA: function (credentials, cb) {
plaidClient.addConnectUser(someParams, someCallBack);
// provideCredentialsToMFA returns nothing and it seems plaidClient.addConnectUser returns nothing too
}
$scope.addUser = function (userInfo) {
// Here is the difference with your 'then' call. Accounts.addUser doesn't return anything and takes 2 params
Accounts.addUser(userInfo).then(someCallBack);
};

Chaining Angular Promises

I am having some trouble chaining promises in Angular. What I want to do is fetch my project object from the API, then check if the project owner has any containers, if they do, trigger the another GET to retrieve the container. In the end the container assigned to scope should either be null or the object retrieved from the API.
Right now, this example below resolves immediately to the second then function, and I get the error, TypeError: Cannot read property 'owner' of undefined. What am I doing wrong?
$http.get('/api/projects/' + id).then(function (data) {
$scope.project = data.project;
return data.project;
}).then(function (project) {
var containers = project.owner.containers;
if (containers.length) {
return $http.get('/api/containers/' + containers[0]);
} else {
return null
}
}).then(function (container) {
$scope.container = container;
});
Ah, turns out the data from passed into then is inside a field, so I needed to do
$scope.project = data.data.project;
return data.data.project;
Your example code works, but what if the $http call fails because of a 404? Or you want to later want to add some extra business logic?
In general you want to handle 'negative' cases using a rejecting promise, to have more control over the chaining flow.
$http.get('/api/projects/' + id).then(function (data) {
$scope.project = data.data.project;
return data.data.project;
}).then(function (project) {
var containers = project.owner.containers;
if (containers.length) {
return $q.reject('containers empty');
}
return $http.get('/api/containers/' + containers[0]);
}).then(function (container) {
$scope.container = container;
}).except(function (response) {
console.log(response); // 'containers empty' or $http response object
$scope.container = null;
});

AngularJS: Chaining promises ignore rejection

I'm new to AngularJS and I would like to know why does AngularJS keeps going on the next chaining operation even if one of my previous chaining functions fails.
// Manipulate data
function manipulationData(data) {
return data.total + 2;
}
/*
* Begin chaining promises
*/
var deferred = $q.defer();
// JSON Request
$http.get('/json_test.json')
// If results is ok, then manipulate some data
.then(function(results) {
if(results.status == "ok") {
return manipulationData(results);
} else {
deferred.reject("Some data error");
}
}, function(reason) {
deferred.reject("Error request: " + reason);
})
// If manipulation is success
.then(function(results) {
if(results > 5) {
return $http.get('http://host.com/second');
} else {
deferred.reject("Error! Data is invalid");
}
}, function(reason) {
deferred.reject("Error request: " + reason);
})
.then(function(result){
return $http.get('http://host.com/second');
})
return deferred.promise;
For some reason, the application keeps executing all the function even if one of them failed. I want the operation to stop when the 1st promise is not working.
For example, if second operation failed, it should throw the error "Some data error".
Thank you
It seems to me that you face a problem only when your error condition fires (i.e. results.status is not "ok"), because you don't return anything in this case. When you don't return anything Q will fallback to the current promise (returned by the last function) and as it succeeded the next method will also be called (as the promise it is hooked to is fulfilled). Here is an example how to handle custom errors with Q:
promise.then(function(data) {
if (p(data)) {
return doSomethingAsync(data);
} else {
return $q.reject("Some error...")
}
}).then(function(data) {
doSomething(data); // This will be called only if p(data) was true.
}).catch(function(err) {
console.log(err); // This will be called only if p(data) was false.
})
$q is a normal AngularJS service, so you just need to add it as a parameter. You may also refer to the docs page - https://code.angularjs.org/1.2.12/docs/api/ng.$http
Just from a brief look at your code, there doesn't seem to be any reason to use $q at all (well it will be used indirectly ofc.
// Manipulate data
function manipulationData(data) {
return data.total + 2;
}
// JSON Request
return $http.get('/json_test.json')
// If results is ok, then manipulate some data
.then(function(results) {
if(results.status == "ok") {
return manipulationData(results);
} else {
throw new Error("Some data error");
}
}, function(reason) {
throw new Error("Error request: " + reason);
})
// If manipulation is success
.then(function(results) {
if(results > 5) {
return $http.get('http://host.com/second');
} else {
throw new Error("Error! Data is invalid");
}
}, function(reason) {
//Note: This will give 'Error request: Error request: {reason}'
// in cases where '/json_test.json' failed
throw new Error("Error request: " + reason);
})
.then(function(result){
return $http.get('http://host.com/second');
});
As promises works more like try - catch blocks than one may think, when you handle the error, but don't throw another in the handler, your actually saying you have recovered (Which was not your intention)...
Here is a plunk where you can see that effect if you remove the first block of code that is commented out.
http://plnkr.co/edit/kegH8Ca3O3EjQqGCi143?p=preview
angular.forEach(self.stages, function(stage) {
promise = promise.then(function() {
stage.state = 'progress';
return $timeout(function() {
stage.state = 'done';
if(stage.id == 3) throw Error("Test");
}, 1000, true);
}
// add this code to see that we recover.
//, function(error) {
// stage.state = 'error';
//}
// add this code to see how we re-throw the error
//, function(error) {
// stage.state = 'error';
// throw error;
//}
);
});

Resources