Angular 2 Observable emit/error functions are ignored after calling error function once - api

I have a API login service using a http service to perform a login logic (LoginApiService, login-api.service.ts):
login(data: LoginCredentials): Observable<LoginResult> {
let body = JSON.stringify( data );
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(`${this.getBaseUrl()}/login`, body, options)
.map(this.extractData)
.catch(function(error: Response){
return Observable.throw(error); // IF YOU TRY TO LOGIN WITH WRONG CREDENTIALS, AN ERROR WILL BE THROWN
});
}
This service is used in a global service class (authService, auth.service.ts):
login(data: LoginCredentials): void {
this.api.login(data).subscribe(
data => {
this.isLoggedIn = true;
this.group = data.group;
this.Login.emit(new LoginResult(this.group)); // THE CALL OF THIS FUNCTION IS IGNORED IF THE "this.Login.error" FUNCTION HAS BEEN CALLED BEFORE
},
error => {
this.Login.error(error); // THIS IS IGNORED TOO, IF IT WAS CALLED BEFORE
}
);
}
Component (LoginComponent, login.component.ts):
ngOnInit() {
this.LoginSubscription = this.authService
.Login
.subscribe( // THE EVENTS ARE NOT FIRED IF THE ERROR EVENT HAS BEEN FIRED BEFORE ONCE
data => {
if ( this.authService.isLoggedIn ) {
this.router.navigate(['/dashboard']);
}
},
error => {
this.handleError(error);
}
);
}
login() {
this.authService.login( new LoginCredentials(this.user, this.password) );
}
Template (login.component.html):
<div>
<label>User: </label>
<input [(ngModel)]="user" placeholder="user">
</div>
<div>
<label>Password: </label>
<input [(ngModel)]="password" placeholder="password" type="password">
</div>
<p>
<button (click)="login()">Login</button>
</p>
If the error event function of the login observable has been called before, calling the emit and/or error function after this will be ignored.
With correct login credentials, the simulated API responses with HTTP
code 200 and everything works fine
On wrong credentials, the HTTP response is 500
After calling the API again (with correct or wrong credentials), the
events are not fired anymore
This means: If you use wrong login credentials you won't be able to try again without re-loading the page.
Is my idea of using observables wrong?
Why is the event stream hung up after calling the error function
once?
Could someone give me a hint to solve this problem (some kind of
workaround eg.)?

This is happening because you are erroring out your EventEmitter. EventEmitters are basically Observables and when it's Observer calls error or complete, it closes the Obersvable. Closed Observables will stop emitting events by design.
To fix your problem, remove this.Login.error(error); in the LoginComponent. so that you are not closing that EventEmitter. Try replacing that with logic to tell the user that the credentials are wrong or some other message to describe the error.

Related

How can I check if Google Identity Service migration was successful?

We have followed the Google Identity migration documentation and replaced the new code but used existing google OAuth client ID.
Previously, before migration we used to get a warning in console, please see the image attached-
Now, after we have added the new code the warning message is not showing in the console. Below is the new console image
But still we have received one reminder mail from Google with our app name and OAuth key that we still need to migrate.
So we are confused if the migration was successful or not? Please help? Do we have to create a new OAuth client ID and replace?
(Please note we are not seeing any warning in console related to this.)
Here is our code snippet-
<div class="bg-google text-white pt-3 pb-2 px-4 rounded-lg cursor-pointer ml-4 mr-4" #click="SocialLogin">
<img src="#/assets/images/pages/login/google.svg"/><span>Sign in with Google</span>
</div>
mounted() {
this.tokenClient= google.accounts.oauth2.initTokenClient({
client_id: process.env.VUE_APP_GOOGLE_OAUTH_KEY,
scope: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
prompt: "consent",
callback: tokenResponse => this.handleCredentialResponse(tokenResponse) // your function to handle the response after login. 'access_token' will be returned as property on the response
});
},
methods: {
SocialLogin() {
this.tokenClient.requestAccessToken();
},
async handleCredentialResponse(response) {
this.$helper.showLoading();
const userInfo = await new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open('GET', `https://www.googleapis.com/oauth2/v3/userinfo`);
xhr.setRequestHeader('Authorization', `Bearer ${response.access_token}`)
xhr.onload = function () {
if (this.status >= 200 && this.status < 300)
resolve(JSON.parse(this.responseText));
else resolve({ err: '404' });
};
xhr.send();
});
if(userInfo){
localStorage.setItem("user_email", userInfo.email);
}
},
}
I checked with the team apparently there is a back log for these messages so they may have detected a few weeks ago that you were still using the old library and the message is just getting sent now.
You know you have upgraded and are no longer getting the error message so your all good.

GET Request fails in Vuejs Fetch but works perfectly in Postman and in browser due to 302 redirection

I have a web application built using NuxtJS/Vuejs within that I have a field where user can provide the URL and my application should make a GET request to that URL and obtain the data. Mostly the URL is related to GitHub from where it should fetch XML/JSON the data.
When I provide a certainly URL in browser/Postman then the redirection happens and data from the redirected URL is loaded. I want to achieve the same in my code but it's not happening and I get the error:
index.js:52 GET {{URL}} net::ERR_FAILED 302
But these URL works perfectly in browser and in Postman without any issue. Following is my code where I am making the request using Vuejs Fetch:
fetch(inputURL, {
method: 'GET'
})
.then((response) => {
console.log('RESPONSE')
console.log(response)
})
.catch((error) => {
console.log('ERROR')
console.log(error.response)
})
Using the Axios:
axios
.get(inputURL)
.then((response) => {
console.log("RESPONSE");
console.log(response);
})
.catch((error) => {
console.log("ERROR");
console.log(error);
})
I tried setting various header, I tried using axios etc but nothing seems to work for me. Can someone please explain to me what am I doing wrong and how to fix this issue? Any help or workaround would be really appreciated.
First of all, the 'Access-Control-Allow-Origin' header is something that should be set up on the server side, not on the client making the call. This header will come from the server to tell the browser to accept that response.
The reason why your code works from postman/browser is because you're not under the CORS rules when you request it like that.
One way around it, would be to make a call to your backend and tell the backend to call GET the data from the URL provided and then return it to your front-end.
Example:
//call_url.php
<?php
$url = $_GET['url'];
$response = file_get_contents($url);
echo $response
?>
//vue.js component
<input type="text" v-model="url"></input>
<button type="button" #click="callUrl">call me</button>
...
methods: {
callUrl() {
axios.get('call_url.php?url=' + encodeURIComponent(this.url))
.then(response => {
//...do something
}
}
}
As mentioned in another answer it's not possible for any library including Fetch and Axios to make requests and obtain the Data due to various security policies. Hence, I created a method in my Spring boot application that will obtain the data from URL and I make a request to my Spring boot using Axios.
import axios from 'axios'
axios.post('/urlDataReader', inputURL)
.then((response) => {
console.log(response)
})
.catch((error) => {
console.log(error)
})
Spring boot app:
//Method to read the data from user-provided URL
#PostMapping(value = "/urlDataReader", produces = "text/plain")
public String urlDataReader(#RequestBody String inputURL) {
final String result = new RestTemplate().getForObject(inputURL, String.class);
return result;
}

Implementing Google One Tap Login using angular

navbar.template.html
<div id="g_id_onload"
data-client_id="832#################m921.apps.googleusercontent.com"
data-cancel_on_tap_outside="false"
data-login_uri="http://localhost:3010/auth/g-one-tap"
data-callback="handleCredentialResponse">
</div>
The API get the response I am able to validate the user and return the validated JWT token, how can I capture the response and avoid the redirection of the page to http://localhost:3010/auth/g-one-tap
How can I us some click function to be used in typsecript file which can help in following the normal login flow which I am using earlier with google login button.
public socialSignIn(responseData) {
this.googleSubscription = this._globalService.googleLogin(responseData)
.subscribe(
data => {
if (data['success']) {
const token = data['data']['token'];
if (this.platformId === 'browser') {
// login to save the token
}
}
},
error => {
console.log('error');
}
);
}
As mentioned here, you should not use both data-login_uri and data-callbck attributes at the same time.
You need to remove the data-login_uri attribute in you code.
And povide an implementation for the callback function (whose name is handleCredentialResponse in your code) if not yet.

problem accessing elements using id/name for login form in cypress

I am trying to login to a form written in angular js but cypress throws the following exception:
Uncaught TypeError: $(...).materialScrollTop is not a function
This error originated from your application code, not from Cypress.
When Cypress detects uncaught errors originating from your application it will automatically fail the current test.
This behavior is configurable, and you can choose to turn this off by listening to the 'uncaught:exception' event.
https://on.cypress.io/uncaught-exception-from-application
This is the cypress login code:
context('TestLogin', () => {
it('Test Login', () => {
cy.visit('url');
cy.get('input[id=Email]').type('email', {force: true});
cy.get('input[id=Password]').type('passcode', { force: true });
cy.get('button[type=submit]').click();
})
})
Since the login has a csrf token, I have used cy.request() as follows and I do get a response with status code 200 but when re-loading the site it goes back to login page.
describe("Tests for AntiForgeryToken", function () {
// variable from config, that contain Identity Server URL
const identityUrl = Cypress.config("identityServerUrl")
// command declaration that we are going to use in tests
// allows us to create request to server
Cypress.Commands.add("loginByToken", function (token, login, password) {
cy.request({
method: "POST",
failOnStatusCode: false,
url: `${identityUrl}/Account/Login`,
form: true,
body: {
email: login,
password: password,
__RequestVerificationToken: token,
RememberLogin: false
}
})
})
it("Should parse token from response body and return 200", function () {
cy.request(`${identityUrl}/Account/Login`)
.its("body")
.then((body) => {
const $html = Cypress.$(body)
// when the page is rendered
// we are trying to find the Request Token in the body of page
const token = $html.find("input[name=__RequestVerificationToken]").val()
// POST request with token and login data
// then we simply verify whether Indentity Server authorized us
cy.loginByToken(token, "test#test.com", "Test_1234")
.then((resp) => {
expect(resp.status).to.eq(200)
})
})
cy.visit(`${identityUrl}/Account/`);
})
Cypress documentation didn't provide much info about the exception.
Any insights from cypress experts are helpful.
As evident from the error, Cypress is failing the test as it found an exception in your application,this is not a cypress level exception but an uncaught exception in your app which is causing cypress to fail the test, this is pretty useful as you can check if its an actual error in your app and log it for the dev team to fix, check if you are able to reproduce this manually, either way i think the application code should be fixed to either fix the bug or catch the exception and return a valuable error message. If you want to disable this feature you can turn off all uncaught exception handling, so in your index.js or whatever file is the entry point add the following:
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
// you can also add a Debugger here to analyze the error
debugger;
return false;
});
not sure if turning this off will help as looks like there is something in your application which could be an issue, but this is just for informational purposes that you can turn this feature off if you needed to.
Here is the documentation for further reading : Cypress Events documentation
hope this helps

Aurelia HttpClient cancel requests

I am trying to build an auto complete component and want to make it cancel unresolved requests to the server while they type.
I can find no documentation around this in the documentation for HttpClient. It mentions it IS cancellable (unlike fetch) but not how. https://aurelia.io/docs/plugins/http-services
Currently I have this which I cobbled together quite blindly, unsurprisingly it doesn't even abort the requests:
async searchTermChanged(newValue, oldValue) {
if (newValue.length < 3)
return;
if (this.promises.length) {
this.promises.forEach(x => x.abort());
//should probably remove too
}
var promise = this.httpClient.post(this.endpoint, { SearchTerm: newValue });
this.promises.push(promise);
var data = await promise;
var response = JSON.parse(data.response);
this.results = response;
}
}
Where can I find out more information on how to make cancellable requests? My google-fu is failing me.
Looks like you can do this:
this.client["pendingRequests"].forEach(request => {
request.abort();
});
I am having to do ["pendingRequests"] as I'm using TypeScript and the array does not seem to be within the definition.
Note: I am also using a scoped HttpClient per autocomplete, so that when it cancels all previous requests it will not accidentally cancel something else that the app is requesting.