How to determine what kind of error message I should return from golang API? - rest

I have a GoLang API with an SPA to consume it. What I do to errors in my API is return them until the handler where I test if an error from previous functions exist. If there is an error, I put it inside the response body, set status code to either 400 or 500 then return the response
in the handler function, to be able to create a clear message to the client side, I need to know what kind of error was returned, how do I do it?
I know about error types but I read about Dave Cheney's recommendation to just return an error along with a message (or wrap them in other words).
But if there are so many kinds of errors which might occur in the API call, then does it mean before returning the response, I need to check them all just to know what message I should return?

The first thing to say about errors is that just because there's an error interface
type error interface {
Error() string
}
Does not mean that the error returned from any given method can only have that method / information attached to it.
One common method is to define your own error interface:
type myError interface {
error // embeds the standard error interface
OtherMethod() string // can define own methods here
}
When writing methods and functions it's really important to return an error and not myError, else you couple that method to your error implementation and cause dependency nightmares for yourself later.
Now that we've decided we can return extra information from error, using our own error interfaces you've got 3 main choices.
Sentinel errors
Error Failure types
Errors with Behaviour
Sentinel errors
Sentinel errors are error values that are defined as package level variables, are exported and allow comparison to check for errors.
package myPackage
var ErrConnectionFailed = errors.New("connection failed")
func Connect() error {
// trimmed ...
return ErrConnectionFailed
}
A consumer of this example could use the connect function:
if err := myPackage.Connect(); err == myPackage.ErrConnectionFailed {
// handle connection failed state
}
You can do a comparison to check if the error returned is equal to the sentinel error of the package. The drawback is that any error created with errors.New("connection failed") will be equal, and not just the error from myPackage.
Error failure types
Slightly better than sentinel errors are error failure types.
We've already seen that you can define your own error interface, and if we say ours is now:
type MyError interface {
error
Failure() string
}
type Err struct {
failure string
}
func (e *Err) Error() string {
// implement standard error
}
func (e *Err) Failure() string {
return e.failure
}
const ConnFailed = "connection failed"
err := &Err{failure: ConnFailed}
In the consumer code you can get an error, check if it implements MyError and then do things with it.
err := myPackage.Connect()
if myErr, ok := err.(myPackage.MyError); ok {
// here you know err is a MyError
if myErr.Failure() == myPackage.ConnFailed {
// handle connection failed, could also use a switch instead of if
}
}
Now you have an idea of what caused the error which is nice. But do you really care what the cause was? Or do you only really care what you want to do to handle that error.
This is where errors with behaviour are nice.
Errors with behaviour
This is similar to defining your own error type, but instead you define methods that report information about that error. Given the example above, do you really care that the connection failed, or do you really only care if you can retry or if you need to error up the call stack again?
package myPackage
// this interface could report if the error
// is temporary and if you could retry it
type tempErr interface {
Temporary() bool
}
func (e *Err) Temporary() bool {
// return if the error is temporary or not
}
Now in the consumer (note you don't need to use the myPackage.tempErr), you can test using type assertions if the error is temporary and handle the retry case:
err := myPackage.Connect()
if tmp, ok := err.(interface { Temporary() bool }); ok && tmp.Temporary() {
// the error is temporary and you can retry the connection
}
To answer the question, it's very hard to say without the specifics of the service that you are trying to implement. But as broad advice, I would try and use the last of the 3 examples as much as possible.
If the consumer of your service sends you some input that's not valid:
err := doThing(...)
if inv, ok := err.(interface { Invalid() bool }); ok && inv.Invalid() {
// input is invalid, return 400 bad request status code etc.
}
If you want to return a specific message to a consumer, you could make that a method of your error type. Warning: this would give your packages knowledge that they are being used in a web service, etc.
err := doThing(...)
if msg, ok := err.(interface { ResponseMsg() string }); ok {
// write the message to the http response
io.WriteString(response, msg.ResponseMsg())
}
TL;DR you would need to handle all the cases, but you can create error types that make the code much easier to work with!

Related

PromiseKit, how to await a finalized promise?

coming from the JS world I'm having a bit of problem wrapping my head around promise kit flavor of promises, I need a bit of help with the following.
Assume I have a function that returns a promise, say an api call, on some super class I await for that promise, then do some other action (potentially another network call), on that parent call I also have a catch block in order to set some error flags for example, so in the end I have something close to this:
func apiCall() -> Promise<Void> {
return Promise { seal in
// some network code at some point:
seal.fulfill(())
}
}
// in another class/object
func doApiCall() -> ? { // catch forces to return PMKFinalizer
return apiCall()
.done {
// do something funky here
}
.catch {
print("Could not do first request"
}
}
now I'm trying to write some unit tests for this functionality, so the response is mocked and I know it will not fail, I just need to await so I can verify the internal state of my class:
// on my test file
doApiCall().done {
// test my code, but I get an error because I cannot pipe a promise that already has a `.catch`
}
How would one go about solving this problem? I could use finally to chain the PMKFinalizer but that feels wrong
Another tangential question would be, is it possible to re catch the error on a higher level, let's say a UI component so it can hold some temporary error state? as far as I see I did not see a way to achieve this.
Many thanks 🙏

onErrorResumeNext type inference failed

If my single errors because of a networkexception return Single.just(false)
If my single errors because of another reason return Single.error
If my single succeeds return the original Single value.
this should be as easy as
getStudent(studentId)
.onErrorResumeNext { if (it is NetworkException) return #onErrorResumeNext Single.just(true)
return Single.error(it) }
Type inference failed. Expected type mismatch SingleSource found Single
Your Single needs to return the same type as your source (I'm assuming getStudent() isn't returning a Boolean). If you want to represent a "success" and "error" states, Kotlin has a Result class just for this.
E.g.
getStudent()
.map { student ->
// Your logic here may look different
Result.success(student)
}
.onErrorResumeNext { error ->
if (error is NetworkException){
Single.just(Result.failure(error))
} else {
Single.error(error)
}
}
This will catch network errors and wrap the exception in a Result, all other exceptions will be propagated downstream. You can then choose how to handle the error in your subscribe method.
Depending on your use case however, you may want to also look into using Maybe or the retry() operator.

How to capture the exception when Assert get failed either in Nunit or MsTest

How to capture Failed Assert exception message or state when using NUnit ir MeTest Assert method.
I was trying to capture "AssertionException" when my assert get failed, how can i capture that using either NUnit or MsTest. since Assert method doesn't return any type. My requirements are Test should continue even when assert get failed to finish remaining assertions, should capture the error and should fail this assert. I was using below code statement. When i use Nunit framework its failing and continuous to next assert buy not able to capture even when i use try.. catch block where as in MsTest its failing, capturing using try..catch block and not continuing to next assertion.
Much appreciated for any help!
public static void ResponseValueAssert(dynamic actualValue, dynamic expectedValue, string nameOfAssert)
{
//var ex = Assert.Throws<AssertionException>(() =>
//Assert.AreEqual(expectedValue, actualValue, "Actual value doesn't match with Expected value {0}", nameOfAssert));
if (ResponseValueAssertImplicit(actualValue, expectedValue, nameOfAssert))
{
Console.WriteLine("\r\nResponse Assert:- {0}: <PASS>", nameOfAssert);
}
else
{
Console.WriteLine("\r\nResponse Assert:- {0}: <<FAIL>>", nameOfAssert);
Console.Error.WriteLine("\r\nResponse Assert:- {0}: <<FAIL>>", nameOfAssert);
// Assert.Fail();
}
Console.WriteLine("Expected Value: {0}.\r\nActual Value: {1}.", actualValue, expectedValue);
}
public static bool ResponseValueAssertImplicit(dynamic actualValue, dynamic expectedValue, string nameOfAssert)
{
try
{
Assert.AreEqual(expectedValue, actualValue, "Actual value doesn't match with Expected value {0}", nameOfAssert);
return true;
}
catch (AssertionException ex)
{
return false;
}
}
It's not useful to ask a question about two completely different pieces of software as one thing. The answer would obviously be different for NUnit and MSTest, which are implemented quite differently these days.
So I'll answer only with respect to NUnit, since I have no idea what you would do with MSTest anyway.
In NUnit, if you want a test to continue so that more than one assertion can be reported on in the same test, you use multiple assertions. That is...
Assert.Multiple(() =>
{
// Put your various asserts here
};
NUnit will report all the failed asserts. At the end of the block, the test will be terminated if any of the asserts failed.
Note that many people will say that more than one assert in a test is a bad idea. I believe it is most of the time but that there are situations, like checking multiple properties of the same object, where it can be useful.
Also, for the record, you should never be catching exceptions that are used internally by the test framework. They are basically hidden implementation details and all your work can be lost in the next release of the software... as has happened already to some people in this case.

EXC_BAD_INSTRUCTION when trying to print error from Swift 4 do-try-catch, skipping specified catch conditions

I'm trying to catch my custom Error, but for some reason my catch statements where I name the error that I know is being thrown, it skips those, goes to the default catch, and then gives me a EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) when I try to do print("Unexpected error \(error)")
Here's some abbreviated code:
This is the error that I have declared in my file that houses the class that I'm calling the method on (the class is called CC8DB):
public enum CC8RSVPError: Error {
case noEventOnDate
case invalidRSVPValue
}
I have a method declared as:
public func rsvpForEvent(_ inEventDate:Date?, forUserID inUserID:String, withValue inRSVPValue:String) throws -> CC8RSVPStatus
In another class were I'm calling this method, I have this:
do {
let rsvpResponse = try self.cc8DB.rsvpForEvent(inRSVPDate, forUserID: String(inMessage.author.id.rawValue), withValue: inRSVPValue);
...(other code to do when this doesn't fail)...
} catch CC8RSVPError.invalidRSVPValue {
...(Report specific error to user)...
} catch CC8RSVPError.noEventOnDate {
...(Report specific error to user)...
} catch {
...(Report general error to user)...
print("Error doing RSVP: \(error)");
}
And finally, in the CC8DB.rsvpForEvent() method, I'm triggering an error that does this:
throw CC8RSVPError.invalidRSVPValue;
The germane part of this method is:
public func rsvpForEvent(_ inEventDate:Date?, forUserID inUserID:String, withValue inRSVPValue:String) throws -> CC8RSVPStatus
{
var retStatus = CC8RSVPStatus(eventDate: nil, previousRSVP: "", newRSVP: "");
var upperRSVPValue:String = inRSVPValue.uppercased();
if (["YES", "MAYBE", "NO"].contains(upperRSVPValue)) {
//...(Code to do things when the info is correct)...
} else {
throw CC8RSVPError.invalidRSVPValue;
}
return retStatus;
}
For my test case where I'm seeing this, the inRSVPValue is "bla", to test what happens when a user doesn't enter a valid status value.
What I'm seeing is that rather than going into the catch that's specific for the CC8RSVPError.invalidRSVPValue case, it's going down to the general catch. In addition, I'm getting the EXC_BAD_INSTRUCTION on the line where I try and print the error value.
I've stepped through it to verify that I am indeed hitting the throw line that I think I am, and I can see in the debugger that the value of error is CC8DB.CC8RSVPError.invalidRSVPValue, but even if I try to do po error from the lldb command, I get the same exception error.
Has anyone seen this or know what I could have done to make do-try-catch not work right?
You could assign a constant named error inside your catch statement and inside the catch block read the constant and figure out what to do with it.
do something like:
} catch let error {
switch error {
case CC8RSVPError.noEventOnDate:
// code
case CC8RSVPError.invalidRSVPValue:
// code
}
}
Ok, I figured it out. I realized that somewhere along the way, some build setting got set so that I was statically linking into the binary (this is a command-line tool, a bot for Discord to be specific).
I saw some warnings about some of the Swift runtime libs being found in both the binary and the XCode developer runtime area, and realized that it might be that the error object was being used both in my CC8DB module in the binary and in the built-modules folder (or something to that effect).
I need to statically link for when I actually deploy the bot to where it's going to run, so I must have turned something on that won't turn off (I deleted the extra flags that I thought turned that on, but that wasn't fixing it).
Basically, I recreated my .xcodeproj file with swift package generate-xcodeproj to clear out whatever I broke, and now it works as expected.
Thanks to everyone who looked at this and offered help (especially #gmogames for his time and help). I'm sure it helped lead me down the path of figuring this out.

NUnit: Execute code upon assertion failure hook

Is there a hook in NUnit to execute code only when assertion fails without catching the exception itself. Basically, it should accept action delegate to be executed when assertion fails and then re-throw exception. Why do I need this?
I need to compare two objects and dump the result on the screen, for easier debugging, when assertion fails.
Something like this works but is a bad hack, The problem is that it eagerly evaluates ProcessCompareError so I have unnecessary overhead, plus it does it no matter if there is an error or not. So, is there overload that will accept the delegate that would be executed when assertion fails?
Assert.That(benefitLimitComparer.Compare(copyBenefitLimit, origBenefitLimit), Is.EqualTo(0),limitError, ProcessCompareError(origBenefitLimit, copyBenefitLimit));
}
}
}
private string ProcessCompareError(BenefitLimit origBenefitLimit, BenefitLimit copyBenefitLimit)
{
Console.WriteLine("Original: ");
ObjectDumper.Write(origBenefitLimit);
Console.WriteLine("Copy");
ObjectDumper.Write(copyBenefitLimit);
return "";
}
I'm not sure how it might be done through a delegate. One alternative is to store the result of the Compare. If the result is false, write out the contents of the objects and then call Assert.Fail()
There is a possibilty to wrap an assert as an Action in a try-catch. In the catch you can handle the additional compare:
public static void ExecuteAssert(Action assert)
{
if (assert == null) return;
try
{
assert();
}
catch (Exception ex)
{
// perform the compare
}
}
As remark: I use a similar method to continue test execution and avoid the entire test to stop, if some non-fatal checks fail. Actually I iterate through a number of actions:
private static void VerifyAll(params Action[] asserts)