I am integrating with Plaid Link and Stripe into a wizard form flow (many pages with many fields on each page). All information that the user enters is stored in a global variable "this.applicationservice.application.applicant". The user hits a payment verification in the middle of this flow, where Plaid pops an IFrame after calling Plaid.create(plaidparameters).open(). When Plaid is initialized it wipes my browser memory and "this.applicationservice.application.applicant" is now undefined.
How can I avoid losing the browser memory when calling the Plaid Initialization?
this.http.post('https://localhost:8080/v1/validateach').subscribe(
response => {
let plaidparameters = {
token: response.linkToken,
onSuccess: function(public_token, metadata) {
// Memory is wiped
console.log(this.applicationservice.application)
}
};
// Works Fine
console.log(this.applicationservice.application)
Plaid.create(plaidparameters).open();
}
);
So the problem is that the scope of this inside the onSuccess callback doesn't extend outside the callback.
One of my colleagues who works a lot more on JavaScript suggested the following:
this.http.post('https://localhost:8080/v1/validateach').subscribe(
response => {
const onSuccess = (public_token, metadata) => {
console.log(this.applicationservice.application);
};
let plaidparameters = {
token: response.linkToken,
onSuccess,
};
// Works Fine
console.log(this.applicationservice.application)
Plaid.create(plaidparameters).open();
}
);
And also added: It might actually be better to define that callback function outside of the invocation of this.http.post so it doesn’t inherit its scope from the plaidparameters object.
Related
I am trying to implement POM in playwright, I have created a class in which I am trying to click two buttons to navigate to a page and then apply URL assertion to check the expected URL. But the URL of the Page isn't changing and the assertion is getting failed. here is the class code:
exports.HomePage = class HomePage {
constructor(page) {
this.page = page
this.login_button = page.getByRole('button', { name: 'Login' })
this.client_login_button = page.getByRole(('link', { name: 'CLIENT LOGIN' }))
}
async gotoHomePage(){
await this.page.goto('https://plentifulapp.com/');
await this.login_button.click
await this.client_login_button.click
}
}
and here is the main test file.
test.only('ValidLogin', async ({ page }) => {
const Home = new HomePage(page);
await Home.gotoHomePage();
await expect(page).toHaveURL('https://app.plentifulapp.com/a/pantries')
Asserting applied in main test file in getting failed.
It appears the culprit is that the click methods aren’t actually being called, you’re just referencing the methods themselves. So the fix hopefully is just putting () after .click which should at least correct that and make sure the clicks are actually happening.
I'm implementing apple-authentication in react native using expo-apple-authentication package.
Below is the code which I'm calling on button's onPress.
async handleSocialLogin() {
const { mutate, BB, onSuccess, navigation } = this.props;
try {
const result = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
Alert.alert(JSON.stringify(result))
// signed in
} catch (e) {
Alert.alert(e)
if (e.code === 'ERR_CANCELED') {
// handle that the user canceled the sign-in flow
} else {
// handle other errors
}
}
}
It should return me authentication-token, Full_Name and Email which I requested in scope but It is giving me null for Full_Name and Email.
As per the documentation:
requestedScopes (AppleAuthenticationScope[]) (optional) - Array of user information scopes to which your app is requesting access. Note that the user can choose to deny your app access to any scope at the time of logging in. You will still need to handle null values for any scopes you request. Additionally, note that the requested scopes will only be provided to you the first time each user signs into your app; in subsequent requests they will be null.
You have probably already logged in once and didn't catch the logs. Subsequent log in will result in this data being null
I'm using angularjs for the front end and rails + devise for authentication on the backend.
On the front end I have added a responseInterceptor to redirect to the /#/sign_in page upon any 401 response from any xhr request and display a growl style pop-up message using toastr.
App.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('securityInterceptor');
}]);
App.factory('securityInterceptor', ['$injector', '$location', '$cookieStore', function ($injector,$location,$cookieStore) {
return function(promise) {
var $http = $injector.get('$http');
return promise.then(null, function(response){
if (response.status === 401) {
$cookieStore.remove('_angular_devise_user');
toastr.warning('You are logged out');
$location.path('/#/sign_in');
}
});
};
});
My problem is, when I click on a page that loads several xhr requests during the controllers initialization, for example:
var products = Product.query();
var categories = Category.query();
var variations = Variation.query();
These are needed for various navigation components and they all fire off in parallel, resulting in several duplicate growl-style messages.
Is there a way to make angular quit on the first 401 and stop execution of the rest of the controller from within the interceptor? In a traditional rails app, there would be a "before_filter" that stops regular execution, preventing the page and queries from loading... what's the best way to do this in angular?
I've been pondering about this problem for my own apps too. A sketch of my thoughts (NOT REAL IMPLEMENTATION, SO BEWARE):
A userData service keeps track of whether the user is logged in + other information (e.g. user name, real user name etc):
App.service("userData", function() {
var currentData = {
loggedIn: false
};
function getCurrent() {
return currentData;
}
// called when the user logs in with the user data
function loggedIn(userData) {
// the object is REPLACED to avoid race conditions, see explanation below
currentData = angular.extend({loggedIn: true}, userData);
}
return {
getCurrent: getCurrent,
loggedIn: loggedIn
};
});
The interceptors keep track of the currentData. If an interceptor receives HTTP 401 and the loggedIn flag is true, it changes the flag to false and redirects to the login view. If an interceptor receives HTTP 401 and the loggedIn flag is false, it does nothing besides rejecting the request, because another interceptor has done the view redirection.
When the user logs in, the currentData is replaced, so as to avoid situations with delayed responses (e.g. call1 and call2 are initiated, call1 responds 401; call2 also results in 401, but the delivery of the actual response is delayed; then the user logs in again; then call2 receives its 401; the second 401 should not overwrite the current state)
App.config(["$provide", "$httpProvider", function($provide, $httpProvider) {
$provide.factory("myHttpInterceptor", ["$q", "userData", "$cookieStore", "toastr", "$location",
function($q, userData, $cookieStore, toastr, $location) {
return {
request: function(config) {
config.currentUserData = userData.getCurrent();
return config;
},
responseError: function(rejection) {
if( rejection && rejection.status === 401 && rejection.config && rejection.config.currentUserData && rejection.config.currentUserData.loggedIn ) {
rejection.config.currentUserData.loggedIn = false;
$cookieStore.remove('_angular_devise_user');
toastr.warning('You are logged out');
$location.path('/#/sign_in');
}
return $q.reject(rejection);
}
};
}
]);
$httpProvider.interceptors.push("myHttpInterceptor");
});
Also note I am using the newer way to register interceptors, as $httpProvider.responseInterceptors seems to be deprecated.
What is the Authentication Strategy for opening new (tab) Window for Oauth Authentication and returning to previous tab (logged In) upon successful authentication?
I am using passportjs authentication strategies for Twitter, Facebook and Google. But all of that does authentication in the same window-tab. Is there a predefined strategy which I can follow to do the above?
I can open the permission window in new account using a(target="_blank"), but it does not return to previous tab upon account authentication (by user).
Edit (based on following answer)
Login Page looks like:-
!!!
html
head
script.(type='text/javascript')
function newWindow (auth_target) {
authWindow = window.open(auth_target);
var authenticationSuccessUrl = "/home"
authWindow.onClose = function () {
alert('onClose called.');
authWindow.close();
window.location = authenticationSuccessUrl;
}
}
body
button(type="button", onclick="newWindow('auth/google');").btnLogin with Gmail
For New Window (home) i wrote:-
!!!
html
head
script(type='text/javascript').
function onAuthSuccess() {
authWindow=window.location;
window.onClose();
}
body(onload='onAuthSuccess();')
Its not a full code i was suppose to write, but even this does not work. If the Home page is called after an intermediate authentication window (by google, for password entry) the above code does not work, and window does not close.
// just as some more information, ignore if not necessary
Also please note, that,
window.close()
works only if the parent window that triggered it open is open. If the current window is a standalone the above window.close() will not close the window. Likewise, the above Home page code fails to close current window.
When creating the new window for authentication you can get the reference to the window object.
var authWindow = window.open("location","");
In the authentication tab you on authentication success you can call a method which would close the window and set a property or call a method on the source window suggesting that authentication is complete.
var authenticationSuccessUrl = "http://auth-sucess.url"
authWindow.onClose = function () {
authWindow.close();
window.location = authenticationSuccessUrl;
}
On the authentication window javascript:
var onAuthSuccess = function (){
window.onClose();
}
Be sure to remove the reference to the authWindow at the end of processing. Having window objects as references could potentially lead to memory leaks in the application.
Here is how I see the code can be changed as: This would just create one popup window and it will get closed after authentication.
You have Login page code as :
!!!
html
head
script.(type='text/javascript')
function newWindow (auth_target) {
authWindow = window.open(auth_target);
authWindow.onClose = function (authenticationSuccessUrl) {
alert('onClose called.');
authWindow.close();
window.location = authenticationSuccessUrl;
}
}
body
button(type="button", onclick="newWindow('auth/google');").btnLogin with Gmail
The newWindow('auth/google') should have the following code.
!!!
html
head
script(type='text/javascript').
function onAuthSuccess(authSuccessUrl) {
window.onClose(authSuccessUrl);
}
function ajaxPost() {
var uname = $("#name");
var pass = $("#password");
$.ajax({
url: "/auth/google/",
type: "POST",
data: {username : uname, password: pass}
}).success(function(data){
var redirectUrl = data.url; // or "/home" as you had mentioned
onAuthSuccess(redirectUrl);
});
}
body
p UserName:
input#name(type="text", name="username")
p Password:
input#password(type="text", name="password")
p: button(type="submit", onclick="ajaxPost()").btnLogin
If you can post through ajax to the google authentication service and get the the success response you can use this method.
Hope this works out well for you.
I built an ajax chat in one of my mvc website. everything is working fine. I am using polling. At certain interval i am using $.post to get the messages from the db. But there is a problem. The message retrieved using $.post keeps on repeating. here is my javascript code and controller method.
var t;
function GetMessages() {
var LastMsgRec = $("#hdnLastMsgRec").val();
var RoomId = $("#hdnRoomId").val();
//Get all the messages associated with this roomId
$.post("/Chat/GetMessages", { roomId: RoomId, lastRecMsg: LastMsgRec }, function(Data) {
if (Data.Messages.length != 0) {
$("#messagesCont").append(Data.Messages);
if (Data.newUser.length != 0)
$("#usersUl").append(Data.newUser);
$("#messagesCont").attr({ scrollTop: $("#messagesCont").attr("scrollHeight") - $('#messagesCont').height() });
$("#userListCont").attr({ scrollTop: $("#userListCont").attr("scrollHeight") - $('#userListCont').height() });
}
else {
}
$("#hdnLastMsgRec").val(Data.LastMsgRec);
}, "json");
t = setTimeout("GetMessages()", 3000);
}
and here is my controller method to get the data:
public JsonResult GetMessages(int roomId,DateTime lastRecMsg)
{
StringBuilder messagesSb = new StringBuilder();
StringBuilder newUserSb = new StringBuilder();
List<Message> msgs = (dc.Messages).Where(m => m.RoomID == roomId && m.TimeStamp > lastRecMsg).ToList();
if (msgs.Count == 0)
{
return Json(new { Messages = "", LastMsgRec = System.DateTime.Now.ToString() });
}
foreach (Message item in msgs)
{
messagesSb.Append(string.Format(messageTemplate,item.User.Username,item.Text));
if (item.Text == "Just logged in!")
newUserSb.Append(string.Format(newUserTemplate,item.User.Username));
}
return Json(new {Messages = messagesSb.ToString(),LastMsgRec = System.DateTime.Now.ToString(),newUser = newUserSb.ToString().Length == 0 ?"":newUserSb.ToString()});
}
Everything is working absloutely perfect. But i some messages getting repeated. The first time page loads i am retrieving the data and call GetMessages() function. I am loading the value of field hdnLastMsgRec the first time page loads and after the value for this field are set by the javascript.
I think the message keeps on repeating because of asynchronous calls. I don't know, may be you guys can help me solve this.
or you can suggest better way to implement this.
Kaivalya is correct about the caching, but I'd also suggest that your design could and should be altered just a tad.
I made a very similar app recently, and what I found was that my design was greatly enhanced by letting the controllers work with the fairly standard PRG pattern (post-redirect-get). Why enhanced? well, because POST methods are built to add stuff to an app, GET methods are supposed to be used to get information without side effects. Your polling should be just getting new messages w/o side effects.
So rather than your $.post call expecting data and handling the callback, what I'd recommend is having your controller expose a method for creating new chat messages via POST and then another method that get the last X chat messages, or the messages since a certain timestamp or whatever.
The javascript callback from the post action, then can update some variables (e.g. the last message id, timestamp of the last message, or even the whole URL of the next message based on the info contained in a redirect, whatever).
The $.post would fire only in response to user input (e..g type in a box, hit 'send') Then, you have (separately) a $.get call from jquery that's set up to poll like you said, and all it does is fetch the latest chat messages and it's callback updates the chat UI with them.
I got my answer here: ASP.NET AJAX CHAT
The names below i am referring to are from above link.
i think the actual problem was with the timestamp thing and asynchronous behaviour of $.post. after calling "GetMessages()" method, even if the previous request to retrive chat message was not complete anathor call to same method used to fire due to setting timeout for "GetMessages()" method outside the $.post method. In my question you can see that timeout for "GetMessages()" method is set outside the $.post method. Now i set the timeout for "GetMessages()" method inside the $.post method. so that next call to "GetMessages()" only occur after 3 seconds of completion of current $.post method. I have posted the code below.
var t;
function GetMessages() {
var LastMsgRec = $("#hdnLastMsgRec").val();
var RoomId = $("#hdnRoomId").val();
//Get all the messages associated with this roomId
$.post("/Chat/GetMessages", { roomId: RoomId, lastRecMsg: LastMsgRec }, function(Data) {
if (Data.LastMsgRec.length != 0)
$("#hdnLastMsgRec").val(Data.LastMsgRec);
if (Data.Messages.length != 0) {
$("#messagesCont").append(Data.Messages);
if (Data.newUser.length != 0)
$("#usersUl").append(Data.newUser);
$("#messagesCont").attr({ scrollTop: $("#messagesCont").attr("scrollHeight") - $('#messagesCont').height() });
$("#userListCont").attr({ scrollTop: $("#userListCont").attr("scrollHeight") - $('#userListCont').height() });
}
else {
}
t = setTimeout("GetMessages()", 3000);
}, "json");
}
I addition to that i also changed few things. As suggested by ignatandrei i placed $("#hdnLastMsgRec").val(Data.LastMsgRec); immediately after function(Data) {.
and also
as said by MikeSW i changed the data retrieval process. Previously i was extracting data on the basis of timespan(retrieve all the data associated with
this room id that has greater timespan than last data retrieved message timespan) but now i keep track of the messageid. Now i retrieve only those data that
has message id greater than last retrieved message id.
and guess what no repeataion and perfectly working chat application so far on my intranet.
I still got to see it's performance when deployed on internet.
i think it solved my problem.
i will still test the system and let u guys know if there is any problem.
By default $.post() caches the results
You can either call $.ajaxSetup ({ cache: false}); before JS GetMessages function call to ensure caching is disabled or change the $.post to $.ajax and set cache attribute to false. In the end $.post() is a short cut to this:
$.ajax({
type: 'POST',
url: url,
data: data,
success: success
dataType: dataType
});