I'm using ngrx/effects with marbles testing. I have a service that uses promises. I want my effect to call the service and handle both successful and error states. I have code like this
Effect:
.mergeMap(() =>
this.service.getThings()
.map((things) => new SetThingsAction(things))
.catch((error) =>
of(new HandleAPIErrorAction(error))
)
)
.catch((error) =>
of(new HandleAPIErrorAction(error))
);
Service:
public geThings() {
return Observable.fromPromise(this.promiseBasedThing.getTheThings());
}
Then a test:
actions = hot("a", { a: new GetThingsAction() });
const response = cold("-#", {});
service.getThings.and.returnValue( response );
const expected = cold("-b", { b: new HandleAPIErrorAction("error") });
expect(effects.getThings$).toBeObservable(expected);
This actually all works. However the double catch in the effect seems clearly bad and probably suggests I don't understand how Observables work. In the real world only the later catch is effective. In a test the first is effective.
Based on this it seems like Promises don't work with marbles tests. This SO question gives an idea on error handling but it seems impossible to test because it has a Promise.
How can I use ngrx/effects with error handling, promises, and testing.
Can answer my own after further research.
https://jsfiddle.net/btroncone/upy6nr6n/
Basically I needed to do the catch in the getThings instead of in the effect.
getThings() {
return Observable.fromPromise(this.promiseBasedThing.getTheThings())
.catch((error) => Observable.of(error));
}
Also learned that it's much easier to solve these problems with a simple rsjx example instead of trying to solve it while using ngrx/effects too. This still has two catch statements, but the test mocking now matches how it works in reality.
Related
Hey I need some help here for How to use timeouts in flutter correctly. First of all to explain what the main goal is:
I want to recive data from my Firebase RealTime Database but need to secure this request api call with an time out of 15 sec. So after 15 sec my timeout should throw an exception that will return to the Users frontend the alert for reasons of time out.
So I used the simple way to call timeouts on future functions:
This functions should only check if on some firebase node an ID is existing or not:
Inside this class where I have declared this functions I also have an instance which called : timeoutControl this is a class which contains a duration and some reasons for the exceptions.
Future<bool> isUserCheckedIn(String oid, String maybeCheckedInUserIdentifier, String onGateId) async {
try {
databaseReference = _firebaseDatabase.ref("Boarding").child(oid).child(onGateId);
final snapshot = await databaseReference.get().timeout(Duration(seconds: timeoutControl.durationForTimeOutInSec), onTimeout: () => timeoutControl.onEppTimeoutForTask());
if(snapshot.hasChild(maybeCheckedInUserIdentifier)) {
return true;
}
else {
return false;
}
}
catch (exception) {
return false;
}
}
The TimeOutClass where the instance timeoutControl comes from:
class CustomTimeouts {
int durationForTimeOutInSec = 15; // The seconds for how long to try until we throw an timeout exception
CustomTimeouts();
// TODO: Implement the exception reasons here later ...
onEppTimeoutForUpload() {
throw Exception("Some reason ...");
}
onEppTimeoutForTask() {
throw Exception("Some reason ...");
}
onEppTimeoutForDownload() {
throw Exception("Some reason ...");
}
}
So as you can see for example I tried to use this implementation above. This works fine ... sometimes I need to fight with un explain able things -_-. Let me try to introduce what in somecases are the problem:
Inside the frontend class make this call:
bool isUserCheckedIn = await service.isUserCheckedIn(placeIdentifier, userId, gateId);
Map<String, dynamic> data = {"gateIdActive" : isUserCheckedIn};
/*
The response here is an Custom transaction handler which contains an error or an returned param
etc. so this isn't relevant for you ...
*/
_gateService.updateGate(placeIdentifier, gateId, data).then((response) {
if(response.hasError()) {
setState(() {
EppDialog.showErrorToast(response.getErrorMessage()); // Shows an error message
isSendButtonDiabled = false; /*Reset buttons state*/
});
}
else {
// Create an gate process here ...
createGateEntrys(); // <-- If the closures update was successful we also handle some
// other data inside the RTDB for other reasons here ...
}
});
IMPORTANT to know for you guys is that I am gonna use the returned "boolean" value from this function call to update some other data which will be pushed and uploaded into another RTDB other node location for other reasons. And if this was also successful the application is going on to update some entrys also inside the RTDB -->createGateEntrys()<-- This function is called as the last one and is also marked as an async function and called with its closures context and no await statement.
The Data inside my Firebase RTDB:
"GateCheckIns" / "4mrithabdaofgnL39238nH" (The place identifier) / "NFdxcfadaies45a" (The Gate Identifier)/ "nHz2mhagadzadzgadHjoeua334" : 1 (as top of the key some users id who is checked in)
So on real devices this works always without any problems... But the case of an real device or simulator could not be the reason why I'am faceing with this problem now. Sometimes inside the Simulator this Function returns always false no matter if the currentUsers Identifier is inside the this child nodes or not. Therefore I realized the timeout is always called immediately so right after 1-2 sec because the exception was always one of these I was calling from my CustomTimeouts class and the function which throws the exception inside the .timeout(duration, onTimeout: () => ...) call. I couldn't figure it out because as I said on real devices I was not faceing with this problem.
Hope I was able to explain the problem it's a little bit complicated I know but for me is important that someone could explain me for what should I pay attention to if I am useing timeouts in this style etc.
( This is my first question here on StackOverFlow :) )
So basically I am using the flutter_uploader package to upload files to a server and I'd like to execute a function after the upload is complete:
final StreamSubscription<UploadTaskProgress> subscription = _uploader.progress.listen(
(e) {
print(e.progress);
},
onError: (ex, stacktrace) {
throw Exception("Something went wrong updating the file...");
},
onDone: () {
myFunction(); // won't run
},
cancelOnError: true,
);
The problem is the onDone function doesn't execute thus meaning myFunction never executes. I've done some digging and I found that onDone gets called when we close the stream but there is no such method on the subscription variable. I have not used streams much and therefore am pretty bad with them.
My question is, how can I run myFunction? once the stream is complete? I thought that onDone would get called when such is the case but I guess not.
Thank you!
I didn't used that package before but I was reading a litle bit about the package and I think you can execute your funciton inside the main block, the other ones are to handle internal processes like stopping a background job or some other external stuff like notify the error to some error monitoring tool, this is what I propose to you:
final StreamSubscription<UploadTaskProgress> subscription =
_uploader.progress.listen(
(e) {
if (e.status is UploadTaskStatus._internal(3)) {
myFunction()
}
print(e.progress);
},
onError: (ex, stacktrace) {
throw Exception("Something went wrong updating the file...");
},
cancelOnError: true,
);
Just to be clear I'm not sure of the specific implementation, is just and idea I get from the docs, seems like the event also contains an status property which has a constant for when the event is completed
https://pub.dev/documentation/flutter_uploader/latest/flutter_uploader/UploadTaskProgress/UploadTaskProgress.html
https://pub.dev/documentation/flutter_uploader/latest/flutter_uploader/UploadTaskStatus-class.html
Hope this helps you :D
I am working on a solution where I am using vertx 3.8.4 and vertx-mysql-client 3.9.0 for asynchronous database calls.
Here is the scenario that I have been trying to resolve, in a proper reactive manner.
I have some mastertable records which are in inactive state.
I run a query and get the list of records from the database.
This I did like this :
Future<List<Master>> locationMasters = getInactiveMasterTableRecords ();
locationMasters.onSuccess (locationMasterList -> {
if (locationMasterList.size () > 0) {
uploadTargetingDataForAllInactiveLocations(vertx, amazonS3Utility,
locationMasterList);
}
});
Now in uploadTargetingDataForAllInactiveLocations method, i have a list of items.
What I have to do is, I need to iterate over this list, for each item, I need to download a file from aws, parse the file and insert those data to db.
I understand the way to do it using CompositeFuture.
Can someone from vertx dev community help me with this or with some documentation available ?
I did not find good contents on this by googling.
I'm answering this as I was searching for something similar and I ended up spending some time before finding an answer and hopefully this might be useful to someone else in future.
I believe you want to use CompositeFuture in vertx only if you want to synchronize multiple actions. That means that you either want an action to execute in the case that either all your other actions on which your composite future is built upon succeed or at least one of the action on which your composite future is built upon succeed.
In the first case I would use CompositeFuture.all(List<Future> futures) and in the second case I would use CompositeFuture.any(List<Future> futures).
As per your question, below is a sample code where a list of item, for each item we run an asynchronous operation (namely downloadAnProcessFile()) which returns a Future and we want to execute an action doAction() in the case that all the async actions succeeded:
List<Future> futures = new ArrayList<>();
locationMasterList.forEach(elem -> {
Promise<Void> promise = Promise.promise();
futures.add(promise.future());
Future<Boolean> processStatus = downloadAndProcessFile(); // doesn't need to be boolean
processStatus.onComplete(asyncProcessStatus -> {
if (asyncProcessStatus.succeeded()){
// eventually do stuff with the result
promise.complete();
} else {
promise.fail("Error while processing file whatever");
}
});
});
CompositeFuture.all(futures).onComplete(compositeAsync -> {
if (compositeAsync.succeeded()){
doAction(); // <-- here do what you want to do when all future complete
} else {
// at least 1 future failed
}
});
This solution is probably not perfect and I suppose can be improved but this is what I found works for me. Hopefully will work for someone else.
I've just started learning about #ngrx/store and #ngrx.effects and have created my first effect in my Angular/Ionic app. It runs ok the first time but if I dispatch the event to the store again (i.e when clicking the button again), nothing happens (no network call is made, nothing in console logs). Is there something obvious I'm doing wrong? Here's the effect:
#Effect() event_response$ = this.action$
.ofType(SEND_EVENT_RESPONSE_ACTION)
.map(toPayload)
.switchMap((payload) => this.myService.eventResponse(payload.eventId,payload.response))
.map(data => new SentEventResponseAction(data))
.catch((error) => Observable.of(new ErrorOccurredAction(error)));
Thanks
It sounds like an error is occurring. In that situation, the action in the observable returned by catch will be emitted into the effect's stream and the effect will then complete - which will prevent the effect from running after the error action is emitted.
Move the map and the catch into the switchMap:
#Effect() event_response$ = this.action$
.ofType(SEND_EVENT_RESPONSE_ACTION)
.map(toPayload)
.switchMap((payload) => this.myService
.eventResponse(payload.eventId, payload.response)
.map(data => new SentEventResponseAction(data))
.catch((error) => Observable.of(new ErrorOccurredAction(error)))
);
Composing the catch within the switchMap will prevent the effect from completing if an error occurs.
You must move map() and catchError() into swithchMap() as following
#Effect()
public event_response$ = this.action$.pipe(
ofType(SEND_EVENT_RESPONSE_ACTION),
switchMap((payload) => {
return this.myService.eventResponse(payload.eventId,payload.response).pipe(
map((data: DataType) => new SentEventResponseAction(data)),
catchError((error) => Observable.of(new ErrorOccurredAction(error)))
})
);
);
Please note that, evetResponse() method inside myService should return an observable in order to use pipe afterward.
In case your method inside service returns Promise, you can convert it into an observable by the use of from in the rxjs package as below:
import { from } from 'rxjs';
...
const promise = this.myService.eventResponse(payload.eventId,payload.response);
const observable = from(promise);
return observable.pipe(...
For more and detail description take a look at this link
The snippet of code below is functional (in the sense that it's working ;-)), but seems lame at best and well...
Can anyone suggest a way to make this more composable or at least less ugly?
The code is based on the examples on this page:
Wrap an Existing API with RxJS
function connect() {
return rx.Observable.create(function (observer) {
mongo.connect('mongodb://127.0.1:27017/things', function(err, db) {
if(err) observer.onError(err);
observer.onNext(db);
});
}).publish().refCount();
}
function getThings(db) {
return rx.Observable.create(function (observer) {
db.collection('things').find().toArray(function(err, results) {
if(err) observer.onError(err);
observer.onNext(results);
observer.onCompleted();
});
return function () {
db.close();
};
}).publish().refCount();
}
connect().subscribe(
function (db) {
getThings(db).subscribe(console.log);
}, function (err) {
console.log(err);
}
);
In this specific example, assuming that getThings() is supposed to happen only once after connect() happens, I would change the implementation of getThings() as such:
function getThings() {
return connect()
.flatMap(function(db) {
return rx.Observable.create(function (observer) {
db.collection('things').find().toArray(function(err, results) {
if(err) observer.onError(err);
observer.onNext(results);
observer.onCompleted();
});
return function () {
db.close();
};
});
});
}
Then you can just subscribe to the getThings() stream:
getThings().subscribe(console.log);
We used flatMap to hide the the connection step inside the whole getThings(). FlatMap's documentation sounds complicated, but it isn't that complicated. It just substitutes an event from the source Observable with another future event. Explained in diagrams, it substitutes each x event with a future y event.
---x----------x------->
flatMap( x => --y--> )
------y----------y---->
In our case, x event is "connected successfully", and y is "got 'things' from database".
That said, there are a couple of different ways of doing this, depending on how the app is supposed to work. It is better to think of RxJS as "Event Bus on steroids" rather than a replacement for chainable Promises, because it really is not the latter.
Developing on RxJS is best if you model "everything that happens in the app" as streams of events. If done properly, you shouldn't see these chainable "do this, then do that, then do that", because ultimately that's an imperative paradigm, and RxJS is capable of more than that. Ideally it should be more about telling what the events are, in a declarative fashion. See this tutorial for more explanations, specially the discourse in the "Wrapping up" section. Also this gist might help.