Can we handle ngxs #Action errors via ofActionErrored() without going to default "ErrorHandler"? - ngxs

I have an async ngxs action that throwsError().
I would like the default error handling mechanism to ignore this thrown error because I will be handling it in my code via ofActionErrored(). However, for other actions, default error handling should still take place.
Right now, both the ofActionErrored() and default error handling (via Angular/Ionic) tries to deal with the error.
The alternative I can think of is to dispatch Xxx_SUCCESS and Xxx_ERROR actions from within the initially dispatched action, something I would like to avoid if i can help it.
Advice appreciated.

There is a feature request that raised a similar concern at the NGXS repo. We've discussed in the core team meeting and we'll focus that for the next release. You can provide your feedback there: https://github.com/ngxs/store/issues/1691

You can use ofActionCompleted which as a result can provide the error, if there is one. An example taken from the code I am working on:
this.actions$.pipe(
ofActionCompleted(GetMe)
).subscribe((data) => {
const errorStatus = data.result.error['status'];
if (!data.result.successful && errorStatus === 403) {
this.snackbar.openFromComponent(TranslateSnakeBarComponent, {
data: {message: 'USER_DISABLED'}
});
}
});

Related

How do I make Apollo Client tell me where in my code the error happened?

I'm learning React/Apollo and when I introduce bugs I get the typical red exceptions in my Chrome console. However, with Apollo, it doesn't tell me where in my code the error began as it does in React or other frameworks. When working with hooks that fire off queries in multiple components it makes it exceedingly slow to find the source of the issue.
Do you use any tricks to debug your Apollo code or can you improve the error feedback in some way?
Here's what I see:
ApolloError.ts:46 Uncaught (in promise) Error: GraphQL error: User is not authenticated
at new ApolloError (ApolloError.ts:46)
at QueryManager.ts:1241
at Object.next (Observable.js:322)
at notifySubscription (Observable.js:135)
at onNotify (Observable.js:179)
at SubscriptionObserver.next (Observable.js:235)
at observables.ts:12
at Set.forEach (<anonymous>)
at Object.next (observables.ts:12)
at notifySubscription (Observable.js:135)
at onNotify (Observable.js:179)
at SubscriptionObserver.next (Observable.js:235)
at httpLink.ts:142
First, I'd like to address the specific error you're seeing in your question.
User is not authenticated would indicate to me that this is not an issue on the client side (most likely) and you are trying to make a query that requires authentication. The reason you aren't authenticated might have something to do with the client, but it's going to be pretty much impossible for any frontend framework to tell you where that issue is.
As far as general techniques for debugging apollo-client issues go? Well, when using #apollo/react-hooks, you will get feedback about errors from the value's the hook returns directly. For example, with the useQuery hook:
const Employee = () => {
const { data, loading, error } = useQuery(LOAD_EMPLOYEE_DATA_QUERY);
if (error) {
console.error(error);
throw error;
}
// ...
}
If something went wrong, either on the client or the server, you would get feedback about that in the error object. This makes it fairly straightforward to debug.
Sometimes, things aren't so simple though. The next place I usually look to when there are apollo-client issues are the Apollo dev tools. It will show you what queries were made and their results.
Finally, if that doesn't work, then I would start digging through the network tab for XHR requests to /(insert your graphql endpoint here). If there is red, then you should look at the console output from your server.
Hope that this helps!
Those errors are actually when you run ApolloClient.query() and have uncaught errors.
This is what I did to handle it. (But ideally you would use useQuery) instead
const apiClient = new ApolloClient({
... // Your ApolloClient options
});
const originalQuery = apiClient.query;
apiClient.query = (...args) => {
// Get the original stack trace instead to figure out where our uncaught error is
const stackTrace = new Error().stack;
return originalQuery(...args).catch(e => {
e.stack = stackTrace;
throw e;
});
};
This way when I get a stack trace it'll actually be where I used this particular Query()

Relevant Http Response Code using graphql-js

Ok here's the thing, I'm trying to figure out how to deal with error handling with graphql-js. (In a case without Relay)
Not specific enough !? Ok so, since graphql-js is catching all errors thrown within resolve functions, I'm kind of confuse on how to deal properly with errors and http responses.
So I had few ideas and would like to know what you think about it !
Always return 200 OK with the graphql response even if containing errors. (Don't like that one)
Switch case on the result.errors[0] and return an http response in respect of the error, returning result.data if no errors. (which could end up being a veeeery long switch case)
Deal with the error handling in the resolve function and throw and object (e.g. { httpCode: 404, msg: 'No X found with the requested id' } )
In the express app.post function(or whatever web framework), having something like:
app.post('/graphql', function(req, res) {
let result = await graphql(req.body);
if(result.errors.size) {
let e = result.errors[0];
res.status(e.httpCode).send(e.msg);
}
res.json(result.data);
}
This doesn't currently work because of the way the error object is marshalled... or at least I haven't found how to get it out of graphql yet. I'm thinking of maybe looking into graphql-js source but I thought I better ask you guys first since I might be missing something obvious.
Obviously, a better idea is welcome !
Cheers :D
I am also trying to figure this out.
The best I have managed to come up with is throwing a custom error in my resolver. Check out apollo-errors. I am not sure if this is the best way, but it could work for you.

Async/Await - not awaiting the async method [duplicate]

This question already has answers here:
Am I right to ignore the compiler warning for lacking await for this async call?
(3 answers)
Closed 7 years ago.
Below is my code. Compiler gives warning because AddLog is not awaited. I do not want to await this call and want to continue executing next lines. I dont have any concern if the exception is consumed also. Is it fine to ignore the warning?
public async Task Add()
{
this.AddLog( "Add executing" );
// Logic to Add Customer
}
public async Task AddLog( string message )
{
// Write to DB
}
Assuming you truly want to call the AddLog method in a fire-and-forget way, then you have a few options.
If, by design, you want AddLog to always be invoked as a fire-and-forget method, then you could change the signature to not return a Task.
public async void AddLog( string message ) // change Task to void
{
// Write to DB
// WARNING: Make sure that exceptions are handled in here.
}
However, if you do this, you better make sure that exceptions are properly handled from within the AddLog method. If any exception goes unhandled, it will crash your process.
Another option is to change the way you invoke AddLog to clearly state your intent that you don't care about when the Task completes, or about any exceptions that may be raised. You can do this by defining an empty continuation (Well, almost empty. See my edit at the bottom of the post for why it's a good idea to read the Task.Exception property at the very least).
// see EDIT for why the Task.Exception property is read here.
this.AddLog("Add executing").ContinueWith(t => { var observed = t.Exception; });
With either option, unless you are awaiting on other code inside your Add method that you are not showing us, then there is no longer any point in defining your Add method as async. You can simply turn it into a regular synchronous method. Otherwise, you'll then get another warning telling you that This async method lacks 'await' operators and will run synchronously....
public void Add() // no need for "async Task"
{
// see EDIT for why the Task.Exception property is read here.
this.AddLog("Add executing").ContinueWith(t => { var observed = t.Exception; });
// Logic to Add Customer
}
In any case, I wouldn't simply ignore the warning. Much like sometimes we get the warning Use of unassigned local variable 'x' in cases where we know that our code is fine, we typically don't ignore the warning. Instead, we may explicitly initialize the variable to null just to make our intent clear, and make the warning go away. Similarly, you can make the warning go away by making your intentions more explicit to the compiler using one of the above options.
EDIT: Word of caution about unobserved exceptions
I should also mention that even with the ContinueWith option, you may have to be careful about unhandled exceptions that come from your AddLog method.
According to this article, the way unobserved exceptions from tasks are handled has changed between .NET 4.0 and .NET 4.5. So, if you are still running .NET 4.0, or if you forcing .NET 4.0 exception behavior via configuration, you run the risk that unhandled exceptions will crash your process whenever the task gets GC-collected and finalized.
To make sure that this is not a problem, you can adjust the continuation to explicitly observe the exception, if any is present. You don't actually need to do anything with it, you just need to read it. This is one way to do it safely:
this.AddLog("Add executing").ContinueWith(t => { var observed = t.Exception; });
I've updated my earlier examples above to use the safer version of the continuation.
I would make add() non async since it isn't...and then task.run on add log

Meteor 0.5.9: replacement for using Session in a server method?

So, I was attempting to do something like the following:
if(Meteor.isServer){
Meteor.methods({connect_to_api: function(vars){
// get data from remote API
return data;
}});
}
if(Meteor.isClient){
Template.myTpl.content = function(){
Meteor.call('connect_to_api', vars, function(err,data){
Session.set('placeholder', data);
});
return Session.get('placeholder');
};
}
This seemed to be working fine, but, of course, now breaks in 0.5.9 as the Session object has been removed from the server. How in the world do you now create a reactive Template that uses a server-only (stuff we don't want loading on the client) method call and get data back from that Method call. You can't put any Session references in the callback function because it doesn't exist on the server, and I don't know of any other reactive data sources available for this scenario.
I'm pretty new to Meteor, so I'm really trying to pin down best-practices stuff that has the best chance of being future-proof. Apparently the above implementation was not it.
EDIT: To clarify, this is not a problem of when I'm returning from the Template function. This is a problem of Session existing on the server. The above code will generate the following error message on the server:
Exception while invoking method 'connect_to_api' ReferenceError: Session is not defined
at Meteor.methods.connect_to_api (path/to/file.js:#:#)
at _.extend.protocol_handlers.method.exception ... etc etc
Setting the session in the callback seems to work fine, see this project I created on github: https://github.com/jtblin/meteor_session_test. In this example, I return data in a server method, and set it in the session in the callback.
There are 2 issues with your code:
1) Missing closing brace placement in Meteor.methods. The code should be:
Meteor.methods({
connect_to_api: function(vars) {
// get data from remote API
return data;
}
});
2) As explained above, you return the value in the session, before the callback is completed, i.e. before the callback method had the time to set the session variable. I guess this is why you don't see any data in the session variable yet.
I feel like an idiot (not the first time, not the last). Thanks to jtblin for showing me that Session.set does indeed work in the callback, I went back and scoured my Meteor.method function. Turns out there was one spot buried in the code where I was using Session.get which was what was throwing the error. Once I passed that value in from the client rather than trying to get it in the method itself, all was right with the world.
Oh, and you can indeed order things as above without issue.

Breezejs How to debug cause of TypeError in query response

I'm attempting to use Breeze to query a ASP.Net Web API endpoint and the query fails - with the data object containing:
internalError: TypeError
arguments: Array[2]
0: "createCtor"
1: null
length: 2
__proto__: Array[0]
get message: function () { [native code] }
get stack: function () { [native code] }
set message: function () { [native code] }
set stack: function () { [native code] }
type: "non_object_property_load"
The data object has a message (and responsetext) property which contains the full json response from the query which looks ok and the metadata thats been generated matches the response - it also records status 200 for the response
So I'm guessing there is some kind of issue mapping the response to an object on the client side?
I'm using the NuGet package for Breeze version 0.85.2
I can get the sample ToDo project to run fine on the same environment
My project does use domain objects, contexts etc all from different assemblies and namespaces but I understood thats supported in this version?
Also that one of the properties is an enum - in the metadata this is defined as {\"name\":\"State\",\"type\":\"Edm.Self.State\",\"nullable\":\"false\"}] but in the response is comes through as an integer
Looking for tips on how to debug this further on the client side
Update
comparing the working sample with my code, the error looks to be coming from this function:
/**
Returns the constructor for this EntityType.
#method getEntityCtor
#return {Function} The constructor for this EntityType.
**/
ctor.prototype.getEntityCtor = function () {
if (this._ctor) return this._ctor;
var typeRegistry = this.metadataStore._typeRegistry;
var aCtor = typeRegistry[this.name] || typeRegistry[this.shortName];
if (!aCtor) {
var createCtor = v_modelLibraryDef.defaultInstance.createCtor;
if (createCtor) {
aCtor = createCtor(this);
} else {
aCtor = function() {
};
}
}
this._setCtor(aCtor);
return aCtor;
};
The defaultInstance property on v_modelLibraryDef is undefined in my running code - what am I missing on the configuration of breeze for that to happen?
Update 2 - Resolved but why
Ok so I got this working - I was missing a reference to knockout (which I was planning to use but hadn't got that far) - I was a little bit misled by the breeze prerequisites which don't mention knockout so if anyone can explain how I could have got this working without knockout and if its a bug then the points are yours
Got same error, and referencing knockout.js helped(I'm using angularjs for my app)
manager.executeQuery(query).then(function(data) {
console.log(data);
});
But.
It seems, that data-mapper works with knockout by default, so we have XHR results as K.O. model with observables.
so I added breeze.config.initializeAdapterInstance("modelLibrary", "backingStore", true);
and now I don't receive data.results as observable collection.
Hope my answer will help.
Sorry you struggled Richard. We'll try to learn from it and spare the next person the pain you endured.
FWIW, we do not say that Knockout is a prerequisite ... because KO is not a prerequisite. You can use Angular or Backbone instead and we anticipate other alternatives in future.
We don't want to drown you in configuration options when you're just learning Breeze. So we picked KO as the default model library (just as jQuery is the default AJAX provider and Web API is the default "dataservice" technology). We say so in numerous places; prerequisites looks like another good place to mention it.
As it happens, you intended to go with KO anyway so no configuration would have been necessary. Most folks start with something like the MVC template which includes KO and loads it for you in the Index.cshtml.
Apparently you started from a clean slate ("ASP Empty Web Application" perhaps?). The Breeze Web API NuGet package strives to be spare and therefore does not include KO. We figured (incorrectly) that you would add it yourself ... in the right script order ... if you wanted to use KO. Clearly we could do a better job of documenting this particular development path ... especially as we like it so much ourselves. Thanks for pointing it out.
The other problem is that the exception was not helpful. You can see from other attempts to answer your question that even folks with Breeze experience couldn't recognize what was wrong. We'll look to see if we can detect the missing script a little earlier and throw an exception with a better message.
This error looks like it has to do with one of your Entity type constructors. I'm guessing that you are calling the 'registerEntityTypeCtor' method somewhere in your code. If so, then I would put a breakpoint in the constructor that you are registering there.
Per your other comment, .NET enums are supposed to get converted into integers on the breeze client. This is the only 'primitive' datatype that could support them. They will get converted back to enums on the server when you call 'EntityManager.saveChanges'
Breeze does not require 'knockout', you can use either 'angularjs' or 'backbone' as well. We simply default the breeze client to knockout if you do not specify another library. See the 'breeze.config.initializeAdapterInstance' topic here. We do need to a better job of documenting this.
Every time I get an error at which the Message property of the response is the data in json format means I have a bug in the function that runs after getting the data.
Example:
dataservice.getPalanca(routeData.PalancaID)
.then(function (data) {
self.palanca(data.results[0]);
})
.fail(function (error) {
console.log(error); /*if I get here and error.Message == correct json almost always means error in .then function*/
toastr.error("Ha ocurrido un error al obtener los datos");
});
I hope I help you.

Resources