algorand / go-algorand-sdk

Algorand Golang SDK

Home Page:https://pkg.go.dev/github.com/algorand/go-algorand-sdk/v2

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Fix API Error Wrappers

winder opened this issue · comments

Subject of the issue

The way API errors are wrapped does not work. Having type aliases to an interface means that at runtime all of the errors are indistinguishable from one another.

Here is the current code:

type BadRequest error
type InvalidToken error
type NotFound error
type InternalError error

// extractError checks if the response signifies an error.
// If so, it returns the error.
// Otherwise, it returns nil.
func extractError(code int, errorBuf []byte) error {
	if code >= 200 && code < 300 {
		return nil
	}

	wrappedError := fmt.Errorf("HTTP %v: %s", code, errorBuf)
	switch code {
	case 400:
		return BadRequest(wrappedError)
	case 401:
		return InvalidToken(wrappedError)
	case 404:
		return NotFound(wrappedError)
	case 500:
		return InternalError(wrappedError)
	default:
		return wrappedError
	}
}

Solution

Create a real error and use that for the specific types. They should also include the error code, for example:

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

type HTTPError struct {
	Msg  string
	Code uint
}

func (e HTTPError) Error() string {
	return fmt.Sprintf("Client error (%d): %s", e.Code, e.Msg)
}

type NotFound HTTPError

func (e NotFound) Error() string {
	return fmt.Sprintf("NotFound error (%d): %s", e.Code, e.Msg)
}

func main() {
	genericError := HTTPError{
		Msg:  "Method Not Found",
		Code: 404,
	}

	fmt.Printf("Error type: %T, Error message: %v\n", genericError, genericError)

	var notFound NotFound = NotFound{
		Msg:  "Method Not Found",
		Code: 404,
	}
	fmt.Printf("Error type: %T, Error message: %v\n", notFound, notFound)
}

Create types so that the extractError can be updated as follows:

// extractError checks if the response signifies an error.
// If so, it returns the error.
// Otherwise, it returns nil.
func extractError(code int, errorBuf []byte) error {
	if code >= 200 && code < 300 {
		return nil
	}

	switch code {
	case 401:
		return InvalidToken{Msg: errorBuf}
	case 404:
		return NotFound{Msg: errorBuf}
	case 500:
		return InternalError(wrappedError)
	default:
		if code < 400 {
			return ClientError{Code: code, Msg: errorBuf}
		} else if code < 500 {
			return ServerError{Code: code, Msg: errorBuf}
		}
		return UnknownError{Code: code, Msg: errorBuf}
	}
}

nice catch!