angular2 wait for error http response - http

I have a problem with angular2 http response.
I want to catch the error in my component.
How does my app work.
In my Component, I Call a function in a personal service :
var response = this.apiUser.login(username, password);
alert(response);
In my Service, I try to auth :
this.http.post(this.httpApiAdress + '/' + this.httpUserAutenticate, body, { headers: contentHeaders })
.subscribe(
response => {
localStorage.setItem('id_token', response.json().token);
this.router.navigate(['home']);
},
error => {
return error.json();
},
() => { }
);
When the auth is ok, all work fine. But when the Auth fail, i can't catch the response in my Component.
(Its undefinied because the alert is executed before the http call...)
Can u help me please !!! (It was working when all the code was only in my Component, but I wanted to slip my code...)
Ty.

Return the observable by using map() instead of subscribe()
return this.http.post(this.httpApiAdress + '/' + this.httpUserAutenticate, body, { headers: contentHeaders })
.map(
response => {
localStorage.setItem('id_token', response.json().token);
this.router.navigate(['home']);
},
);
and then use subscribe where you want to execute code when the response or error arrives
var response = this.apiUser.login(username, password)
.subscribe(
response => alert(response),
error => alert(error),
);

Related

SendGrid with Firebase Cloud Functions error: "socket hang up"

I have a Cloud Function triggered by a pub/sub event. I use sendgrid nodejs api. The main idea is sending my clients a weekly stats email. sendEmail() function run for each client (80 times). But when I check function logs I see that 25-30 of client emails are sent with success but the remaining it gives that error: "socket hang up"
I shortened the whole code to show the main part related sending email. Here is the last part.
// I shortened the whole function as it is a very long function.
// The main and the last part is as below
// I have nearly 80 clients and sendEmail function run for each client.
function calcData(i, data) {
return admin.database().ref('clientUrlClicks/' + data.key)
.orderByChild('date')
.startAt(dateStartEpox)
.endAt(dateEndEpox)
.once('value', urlClickSnap => {
clients[i].clickTotalWeek = urlClickSnap.numChildren();
clients[i].listTotalWeek = 0;
admin.database().ref('clientImpressions/' + data.key)
.orderByKey()
.startAt(dateStart)
.endAt(dateEnd)
.once('value', snap => {
snap.forEach(function(impressionSnap) {
clients[i].listTotalWeek += impressionSnap.val();
})
}).then(resp => {
return sendEmail(i, clients[i]);
}).catch(err => {
console.log(err);
});
}).catch(err => {
clients[i].clickTotalWeek = 0;
console.log(err);
});
}
function sendEmail(i, data) {
var options = {
method: 'POST',
url: 'https://api.sendgrid.com/v3/mail/send',
headers:
{
'content-type': 'application/json',
authorization: 'Bearer ' + sgApiKey
},
body:
{
personalizations:
[{
to: [{ email: data.email, name: data.name }],
dynamic_template_data:
{
dateStart: xxx,
dateEnd: xxx,
}
}],
from: { email: 'info#xxx.com', name: 'xxx' },
reply_to: { email: 'info#xxx.com', name: 'xxx' },
template_id: 'd-f44eeexxxxxxxxxxxxx'
},
json: true
};
request(options, function (error, response, body) {
if (error) {
console.log("err: " + error);
return;
}
return;
});
}
Edit:
In addition to answers below related to "chaining the promises correctly", I also added all emails and personalizations to "personalizations" array as an object on "sendEmail" function. So, instead making a request for each email I make one request. No problem now.
You are not chaining the promises correctly and therefore not returning a final promise at the end of the chaining, which is mandatory for a Cloud Function.
The following set of modifications is a first attempt to solve this problem.
Also, it is not crystal clear how do you call Sendgrid and return the Promise returned by the Sendgrid call. I would suggest that you use the send() method, which returns a Promise, as explained in the doc of the Sendgrid v3 Web API for Node.js, see https://github.com/sendgrid/sendgrid-nodejs/tree/master/packages/mail.
function calcData(i, data) {
//Declare clients aray here
return admin.database().ref('clientUrlClicks/' + data.key)
.orderByChild('date')
.startAt(dateStartEpox)
.endAt(dateEndEpox)
.once('value')
.then(urlClickSnap => {
clients[i].clickTotalWeek = urlClickSnap.numChildren();
clients[i].listTotalWeek = 0;
return admin.database().ref('clientImpressions/' + data.key) //Here you didn't return the promise
.orderByKey()
.startAt(dateStart)
.endAt(dateEnd)
.once('value');
.then(snap => {
snap.forEach(function(impressionSnap) {
clients[i].listTotalWeek += impressionSnap.val();
})
return sendEmail(i, clients[i]);
}).catch(err => {
clients[i].clickTotalWeek = 0;
console.log(err);
return null;
});
}
I see two issues with your code related to promise chaining, which may be causing this problem.
First is that you are using request with callback in your sendEmail function. This will simply not wait for your network call to finish and returns the function. Now this will build up the calls in parallel and before you hit your 80 clients counts the execution of your cloud function will finish. The solution would be to use request-promise-native (https://github.com/request/request-promise-native) library with your request library. So your sendEmail Function will now become
sendEmail (i, data) {
.
.
.
return rpn(options).then((d)=>{return d}).catch((e)=>{return console.log(e)})
}
Other solution is to use sendgrid client for nodejs which will simply return the promise and you don't need to use request. https://github.com/sendgrid/sendgrid-nodejs/tree/master/packages/mail
Second issue is in you call for data read from firebase where you are also using callbacks instead of promises. Correct solution will be:
function calcData(i, data) {
return admin.database().ref('clientUrlClicks/' + data.key)
.orderByChild('date')
.startAt(dateStartEpox)
.endAt(dateEndEpox)
.once('value').then( urlClickSnap => {
clients[i].clickTotalWeek = urlClickSnap.numChildren();
clients[i].listTotalWeek = 0;
return admin.database().ref('clientImpressions/' + data.key)
.orderByKey()
.startAt(dateStart)
.endAt(dateEnd)
.once('value').then( snap => {
snap.forEach(function(impressionSnap) {
clients[i].listTotalWeek += impressionSnap.val();
})
return sendEmail(i, clients[i]);
})
.catch(err => {
console.log(err);
});
}).catch(err => {
clients[i].clickTotalWeek = 0;
console.log(err);
});
}
This will make sure that you function calcData returns after finishing the execution of all the promises chained.
One more thing if you are calling calcData in a loop, then make sure that you store all the promises in an array and after loop call Promise.all(promisesArray), so that you function waits for all the executions to finish.

Google:assistance the final response did not set

we have Google Assistance project, that is working fine, for every intent,
now i want to get dynamic data from web service and return, for that, I am using request module, but its giving error
the final response did not set
below is my code
app.intent('doctor_list', (conv, {doctor}) => {
Request.get("url", (error, response, body) => {
if(error) {
con.ask('data return')
}
con.ask('err')
});
})
You aren't clear in what your Request object is, but I suspect the problem is that you're not returning a Promise object from your call. Network calls are handled asynchronously in node.js, and when you make an async call, you need to return a Promise object.
The easiest way to handle this is using the request-promise-native package. Your code might look something like this:
const Request = require('request-promise-native');
app.intent('doctor_list', (conv,{doctor}) => {
return Request.get('url')
.then( body => {
conv.ask( 'data return' );
})
.catch( err => {
console.log( err );
conv.close( 'error' );
});
});

angular2 How to store response header data from http.get in extra variable?

I'm quite new to Angular 2 and Typescript and want to build a Card-Search App.
Basically I have a component where the response data is stored and observed...
this.searchFormGroup.valueChanges
.debounceTime(200)
.distinctUntilChanged()
.switchMap(searchFormGroup => this.mtgService.getCardsByName(searchFormGroup))
.subscribe(
searchResult => {
this.searchResult = searchResult.cards;
},
err => {
console.log(err);
});
...and a service, which sends the http.get request to the API ...
getCardsByName(searchFormGroup){
this.params.set(...) ...
return this.http.get(this.nameUrl, { search: this.params })
.map(res => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'))}
... communicating with each other.
What's the best way to store header-data from the request inside the component? Basically I need the total card-count and paging-links of the request, which are available inside the response header. I've been trying to find a solution to this for hours :o
Ok try and error got me a solution to this problem:
component activated service's http.get -> service responded with res.json() -> component only got res in JSON, what seems to delete the response header.
My workaround is:
Component:
this.searchFormGroup.valueChanges
.debounceTime(200)
.distinctUntilChanged()
.switchMap(searchFormGroup => this.mtgService.getCardsByName(searchFormGroup))
.subscribe(
res => {
console.log(res.headers); //works fine now!
this.searchResult = res.json().cards;
},
err => {
console.log(err);
});
Service:
getCardsByName(searchFormGroup){
this.params.set(...) ...
return this.http.get(this.nameUrl, { search: this.params })
.catch((error:any) => Observable.throw(error.json().error || 'Server error')) }
So the full res gets passed back to the component now.
However: any tips to make this better?

Chaining RxJS Observables with interval

my first question to the community out here!
i'm working on an app which does communicates to the API in the following way
step1: create request options, add request payload --> Post request to API
API responds with a request ID
Step2: update request options, send request ID as payload --> post request to API
final response: response.json
Now the final response can take a bit of time, depending on the data requested.
this can take from anywhere between 4 to 20 seconds on an average.
How do i chain these requests using observables, i've tried using switchmap and failed (as below) but not sure how do i add a interval?
Is polling every 4 second and unsubscribing on response a viable solution? how's this done in the above context?
Edit1:
End goal: i'm new to angular and learning observables, and i'm looking to understand what is the best way forward.. does chaining observable help in this context ? i.e after the initial response have some sort of interval and use flatMap
OR use polling with interval to check if report is ready.
Here's what i have so far
export class reportDataService {
constructor(private _http: Http) { }
headers: Headers;
requestoptions: RequestOptions;
payload: any;
currentMethod: string;
theCommonBits() {
//create the post request options
// headers, username, endpoint
this.requestoptions = new RequestOptions({
method: RequestMethod.Post,
url: url,
headers: newheaders,
body: JSON.stringify(this.payload)
})
return this.requestoptions;
}
// report data service
reportService(payload: any, method: string): Observable<any> {
this.payload = payload;
this.currentMethod = method;
this.theCommonBits();
// fetch data
return this._http.request(new Request(this.requestoptions))
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body || {};
}
private handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
in my component
fetchData() {
this._reportService.reportService(this.payload, this.Method)
.switchMap(reportid => {
return this._reportService.reportService(reportid, this.newMethod)
}).subscribe(
data => {
this.finalData = data;
console.info('observable', this.finalData)
},
error => {
//console.error("Error fetcing data!");
return Observable.throw(error);
}
);
}
What about using Promise in your service instead of Observable, and the .then() method in the component. You can link as much .then() as you want to link actions between them.

Angular2 - How to chain async service calls (http requests) in a component?

I have a component which first need to call a service that POST something. Then in the same component I want to wait until the POST is done, to call another service which GETs data.
How can I make the GET call wait for the POST call to finish?
In new-version.component.ts:
private createNewVersion(value) {
...
// create new version, then call on all available versions
// POST call
this._newVersionService.createNewVersion(vnr);
// GET call
this._versionService.getAvailableVersions();
...
}
In new-version.service.ts:
export class NewVersionService {
response$: Subject<any>;
constructor(private _http: Http) {
this.response$ = new BehaviorSubject<any>(null);
}
public createNewVersion(versionNr) {
this._http.post('http://localhost:8080/services/' + versionNr, null, {
method: 'POST',
})
.subscribe(response => {
this.response$.next(response.status);
},
error => console.error(error));
}
Thanks!
When a call returns a Promise chain the calls with
someFunction() {
return returnsPromise()
.then(result => doSomethingNext())
.then(result => doSomethingAfterThat());
}
Ensure you have a return that returns the Promise of that chain so the caller of someFunc() also has a chance to time additional work to execute after doSomethingAfterThat() is completed.
When a call returns an Observable then use the complete callback
someFunction() {
return returnsObservable()
.subscribe(
event => doForEachEvent(),
error => handleError(),
() => doSomethingNext()
.then(result => doSomethingAfterThat());
}
doSomethingNext() is executed after the last event and doSomethingAfterThat() is again chained with then() to show how to mix observable and promise. doSomething().
You should be able to concat to achieve sequence, and reduce to collect the emitted values:
var a = this._newVersionService.createNewVersion(vnr);
var b = this._versionService.getAvailableVersions();
Rx.Observable.concat(a, b).reduce((acc:Array<any>, x:any) => {
acc.push(x); return acc;
}, []).subscribe(t=> {
var firstEmitted = t[0];
var secondEmitted = t[1];
});
You can do like this:
Change createNewVersion to:
public createNewVersion(versionNr) {
return this._http.post('http://localhost:8080/nod_inspection_plugin/services/' + versionNr, null, {
method: 'POST',
});
}
Then in your call:
this._newVersionService.createNewVersion(vnr).subscribe(response=> {
this._versionService.getAvailableVersions();
}, error => console.error(error));
Another way to do the same is to subscribe in the new-version.component.ts and call you GET request from within the POST request i.e check whether your POST request is done Correctly or not
if yes POST is done Properly then call you GET request. As below:
In new-version.component.ts:
private createNewVersion(value) {
...
// create new version, then call on all available versions
// POST call
this._newVersionService.createNewVersion(vnr)
.subscribe((res) => {
if(res){
console.log(res);
if (---Post request done properly check via status or something else here----{
CALL YOUR GET REQUEST HERE.....
// GET call
this._versionService.getAvailableVersions();
}
else {
DO something else whatever you want....
}
}
});
...
}
In new-version.service.ts:
export class NewVersionService {
response$: Subject<any>;
constructor(private _http: Http) {
this.response$ = new BehaviorSubject<any>(null);
}
public createNewVersion(versionNr) {
this._http.post('http://localhost:8080/nod_inspection_plugin/services/' + versionNr, null, {
method: 'POST',
})
.map(response => {
return [{status: response.status, json: response.json()}];
},
error => console.error(error));
}
for more info related to http request you can read here.
Better use switchMap() here.
const versions$ = this._newVersionService.createNewVersion(vnr)
.switchMap(response => this._versionService.getAvailableVersions());
versions$.subscribe(response2 => this.versions = response2)
But the problem will be if you make another POST request before first has been resolved, the previous request will get cancelled.

Resources