looker-open-source / sdk-codegen

One SDK to rule them all, and in the codegen bind them
MIT License
232 stars 195 forks source link

Golang SDK v3 does not return error context from API #795

Open Jberlinsky opened 3 years ago

Jberlinsky commented 3 years ago

When issuing a request to the Golang API that triggers an HTTP 4XX error (e.g. a 422 validation error), the Swagger API will return useful error context, e.g.:

{
  "message": "Validation Failed",
  "errors": [
    {
      "field": "dialect",
      "code": "missing_field",
      "message": "This field is required.",
      "documentation_url": "http://docs.looker.com/"
    },
    {
      "field": "database",
      "code": "missing_field",
      "message": "This field is required.",
      "documentation_url": "http://docs.looker.com/"
    },
    {
      "field": "db_timezone",
      "code": "invalid",
      "message": "Must specify a database timezone when user timezones are activated.",
      "documentation_url": "http://docs.looker.com/"
    },
    {
      "field": "max_connections",
      "code": "invalid",
      "message": "Max connections must be an integer between 5 and 100.",
      "documentation_url": "http://docs.looker.com/"
    }
  ],
  "documentation_url": "http://docs.looker.com/"
}

However, the Golang SDK doesn't seem to decode these error messages when a failure is encountered (see https://github.com/looker-open-source/sdk-codegen/blob/main/go/rtl/auth.go#L138). This leaves a user of the SDK in the unfortunate position of having to:

  1. Dump the request object to JSON
  2. Go to the Looker Swagger UI and enter the transposed JSON request
  3. Inspect errors there
  4. Make changes in the Golang SDK caller
  5. Repeat as needed

This also makes it difficult to write effective integration tests for erroneous states.

jeremytchang commented 2 years ago

Hey! This is a great call out.

We already have generated Error and ValidationError struct types.

We can make use of these types and json decode the error responses. To follow error conventions and not break downstream dependencies, we'd need to define a new Error struct that implements the error interface .

Something like this:

type ResponseError struct {
  StatusCode int
  Error v4.Error
  ValidationError v4.ValidationError
}

func (r *ResponseError) Error() string {
    return fmt.Sprintf("...")
}

Though this would couple a custom Go SDK typing to our swagger spec. So not ideal, but also it's unlikely the swagger spec errors will change.