I wrote a test for the following method:
private _segmentationStatus$: Subject<string> = new Subject();
public readonly segmentationStatus: Observable<string> = this._segmentationStatus$.asObservable();
/**
* Get the status from the segmentation service.
*/
public getStatus() {
const segmentationId: string = this._segmentationIdService.segmentationId;
const segmentationStatusUrl = `${this.getSegmentationStatusUrl}/${segmentationId}/status`;
this._http
.get(segmentationStatusUrl, { responseType: 'text' })
.subscribe({
next: () => {
this._loggerService.trackTrace(`Segmentation status: ${status}`, LogSeverity.Verbose);
this._segmentationStatus$.next(status);
},
error: (error) => {
this._messageService.emitMessage(new Message(MessageCode.ErrorGetStatus, error));
this._loggerService.trackException(error);
this._segmentationStatus$.error(error);
}
});
}
To test the method I started to write a test as below:
fit('should emit a message if the request failed', done => {
let messageReceived: Message;
spyOn((service as any)._messageService, 'emitMessage').and.callFake(message => {
messageReceived = message;
});
service.segmentationStatus.subscribe({
error: () => {
expect(messageReceived.messageCode).toBe(MessageCode.ErrorGetStatus);
done();
}
});
spyOnProperty(
(service as any)._segmentationIdService,
'segmentationId',
'get'
).and.returnValue('id');
service.getStatus();
httpTestingController
.expectOne(`${service.getSegmentationStatusUrl}/id/status`)
.flush('{}', { status: 500, statusText: 'status' });
});
If I use the debugger the flow of the program and test is as expected. I see the emitted message and the error handler in the unit test is called. However, after the error handler in the unit test completes the line this._segmentationStatus$.error(error); in the productive code throws another error, which is not handled. Thus the tests fails with the message Uncaught [object Object] thrown.
I`ve read the RxJS documentation and googled the problem, but was not able to figure out what I need to do pass the test. Any help to point out where I did the mistake would be highly appreciated.
I've found the problem; there was another subscriber to segmentationStatus which did not handle the error. As nobody "trapped" the exception it bubbled up and was re-thrown. As explained in On The Subject Of Subjects (in RxJS).
Related
I use rxjs and socket.io client.
Id like to solve this problem.
socket is connected
user send data
socket is having any network error during delivery
delivery is faled
socket throw error to user
Here is my code. how to handle network errors indise Observable?
private sendData(data: any, event: SOCKET_EVENTS): Observable<any> {
return new Observable<any>(observer => {
this.socket
.emit(event, data, function(responseData: Result<any>) {
console.log("Data sended", responseData);
if (responseData.success === true) {
observer.next(responseData.data);
observer.complete();
} else {
console.error(" this.socketData not sended", responseData);
observer.error(data);
}
})
});
}
You can handle errors globaly using event error or something else. For every single emit you can use acknowledgements. Anyway, procedure which handle errors should be more complex. For example:
private sendData(data: any, event: SOCKET_EVENTS): Observable<any> {
return new Observable<any>(observer => {
// set timeout before call error
let errorTimeout = setTimeout(() => {
console.error(" this.socketData not sended");
observer.error();
}, 5000);
this.socket
.emit(event, data, (responseData: Result<any>) => {
console.log("Data sended", responseData);
observer.next(responseData.data);
observer.complete();
// reset timer
clearTimeout(errorTimeout);
})
});
}
We wait 5 seconds for response with acknowledgement. Also you should add logic to handle global errors.
I'm having trouble promises and observables. I have a handful of http requests which are defined in a package using promises. In the rest of my code I am using observables for various things, including other http calls. In one particular section I am checking to see if the user's bearer token is expired and if so then I get a new token and then proceed with the rest of the call.
if (!token || token.exp < Math.round((new Date()).getTime() / 1000)) {
from(this._store.refreshBearerToken())
.pipe(flatMap(resp => {
let newToken = resp.data;
newToken.exp = (new Date()).getTime() / 1000 + newToken.expires_in;
localStorage.setItem('token', JSON.stringify(newToken))
options = options || {};
options.headers = new HttpHeaders({
"Authorization": `${newToken.token_type} ${newToken.access_token}`,
"Content-Type": "application/json"
});
return this._http$.request<T>(method, url, options as Object).pipe(share());
}));
}
Bearer Token method:
async refreshBearerToken() {
const response = await this._q2.sources.requestExtensionData({
route: "refreshBearerToken"
});
console.log(response);
return response;
}
Since this._store.refreshBearerToken returns a promise I wrapped the call in a from to convert it to an observable. This compiles but when it runs I get "Cannot read property 'pipe' of undefined".
How can I convert this promise to an observable so that I can refresh the token and then continue with the rest of the call?
Edit:
I am importing from via import { Observable, from } from "rxjs";.
So, I thought the error was coming from the line .pipe(flatMap(resp =>... but I was wrong. The error is coming from the method which is calling this.
GetInitialLinkList(): Observable<Institution[]>
{
let base = { 'MemberId': localStorage.getItem('memberId') };
let ins = localStorage.getItem("initialInstitutionList");
if (ins)
{
return of(JSON.parse(ins));
}
return this._settingsService.get().pipe(
flatMap(settings =>
{
this._settings = settings;
return this._api.request<Institution[]>("Post", `${this._settings.mea}/GetInitialLinkList`, { body: base })
.pipe(
retry(1),
catchError(this.handleError)
)
.pipe(flatMap(instList =>
{
localStorage.setItem("initialInstitutionList", JSON.stringify(instList));
return of(instList);
}))
}));
}
and that is being subscribed to inside my component:
private GetLinkList()
{
this.showWaiting.emit(true);
this._data.GetInitialLinkList().subscribe((result) =>
{
this.initialList = result;
this.showWaiting.emit(false);
});
}
From what Brandon said (I forgot to return /facepalm...) I added the return so I have return from(this._store.refreshBearerToken()) which changed my error to
ERROR Error Code: undefined
Message: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
defaultErrorLogger # core.js:6014
Can you show the actual error and the line in the code that the error occurs on? Also show where and how you import from.
I notice your code snippet does not return the observable it builds up via from(...).pipe(...) nor does it subscribe to it. It might help to show how your code actually uses this observable.
I am observing something that I cannot see/explain. I did talk to a second pair of eyes and did my due diligence googling. What am I missing - as the title says, onNext and onError aren't called, but onComplete is when back end returns HTTP500 (endpoint throws 500 for the purpose of testing angular error handling). Why?
Service:
delete(item: Item): Observable<any> {
return this.http.post(this.url("delete"), item, { headers: this.header });
}
Component:
this.itemDataService.delete(this.item)
.subscribe(
() => {
alert("result");
..;
},
err => {
alert("Error");
},
() => {
alert("complete");
...;
}
);
you probably have an interceptor swallowing your errors somewhere.. only way this could happen if that's the native http client.
I'm struggling to figure out why my queries will not work when called from Parse.Cloud triggers.
I want to define some logic after an object of particular class was saved (the class is 'Message' in my case).
I'm testing the following simple code in my cloud/main.js:
const conversationQuery = new Parse.Query('Conversation');
conversationQuery.get('myIdHere', { useMasterKey: true })
.then(conversation => {
console.log('### Conversation is', conversation);
})
.catch(err => {
console.log('### Error is', err);
});
Parse.Cloud.afterSave('Message', req => {
const conversationQuery1 = new Parse.Query('Conversation');
conversationQuery1.get('myIdHere', { useMasterKey: true })
.then(conversation => {
console.log('>>> Conversation is', conversation);
})
.catch(err => {
console.log('>>> Error is', err);
});
});
And when I start my instance of parse-server, the following is logged to the console:
### Conversation is { myObjectHere }
However, when I save any object of 'Message' class, I get an error:
>>> Error is { Error: Object not found. <stacktrace here>, message: 'Object not found.', code: 101 }
I'd expect it to log the very same object that was retreived when the server started but instead, it returns a '101 object not found' error.
I think I configured everything according to the documentation but there's a possibility I just missed something.
I'm using Parse Server 3.1.3 and Parse JS SDK 2.1.0
https://github.com/parse-community/parse-server#logging it depends on how you start your parse server, but generally something like: VERBOSE="1" parse-server
The following test is not working with mocha-chai, it is able to to get the input request but throws the error message.
it('/hb : ', function (done) {
return chai.request(app)
.post('/hb')
.send({"a":1 })
.then(function (res) {
expect(err).to.be.null;
expect(res).to.have.status(200);
// { ah: { rt: [Object] }, ad: { mojo: 1 } } }
//console.log("CAlling DOne ........... +");
done();
}, function (err) {
//console.log(err);
throw err;
});
});
Output:
Web Requests : /hb : :
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
The functions that chai-http adds to chai return promises. In your code you return the promise, which is good. However, you also declare your test to take the a parameter: function (done). This would be fine if you did not return the promise, but returning the promise is really the better mechanism here. When you declare your test to take a parameter, Mocha ignores the return value from the test, and so the promise is ignored. So just remove your use of done.
Here's an example that reproduces the error you had in your original code with err being undefined in the function you pass to then.
'use strict';
var app = require('./server');
var chai = require('chai');
chai.use(require('chai-http'));
var expect = chai.expect;
it('/hb', function () {
return chai.request(app)
.post('/hb')
.send({a: 1})
.then(function (res) {
expect(err).to.be.null;
expect(res).to.have.status(200);
});
});
If the server returns a 200 status, then you'll get this on the console:
1) /hb
0 passing (26ms)
1 failing
1) /hb:
ReferenceError: err is not defined
at test.js:13:20
If the server returns a 400 status, the output would be:
1) /hb
0 passing (24ms)
1 failing
1) /hb:
Error: Bad Request
at Test.Request.callback (node_modules/superagent/lib/node/index.js:792:17)
at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:990:12)
at endReadableNT (_stream_readable.js:913:12)
you need to add following:
.set('content-type', 'application/x-www-form-urlencoded')
you can reference this question over Post request via Chai