forked from google/jsonapi
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Handle type errors properly during unmarshal node.
During the `unmarshalNode` routine, various type errors may come up. Currently they are being swallowed by the error handling mechanism. Rework that system so that errors are actually exposed and can be corrected without having to dive into the code. Handle time.Time & *time.Time type errors. Add test for unmarshalling ptr with bad type. Final `attr` type check catch-all & tests. Implement system for JSON API compatible errors. This commit creates a series of structs and interfaces for representing JSON API compatible error objects. It includes a method for emitting a payload of error objects (according to the JSON API spec). Its algorithm is based upon a series of interfaces which define incremental levels of support for errors which are JSON API compatible. Use new error type for unmarshalling errors. Update some docs & the privacy of a func. Don't be prescriptive on `meta`. Use map[string]interface{}. Add a README section on errors &c. Do not force inclusion of `title` & `detail` (use omitempty). Update README & docs on use of pertient errors related types. Update tests to account for these changes. If error type is already *ErrorObject, return immediately. Rm everything except for ErrorsPayload, ErrorObject & MarshalErrors. Also update README & tests. Remove a test fixture which is no longer needed.
- Loading branch information
Showing
5 changed files
with
274 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package jsonapi | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
) | ||
|
||
// MarshalErrors writes a JSON API response using the given `[]error`. | ||
// | ||
// For more information on JSON API error payloads, see the spec here: | ||
// http://jsonapi.org/format/#document-top-level | ||
// and here: http://jsonapi.org/format/#error-objects. | ||
func MarshalErrors(w io.Writer, errorObjects []*ErrorObject) error { | ||
if err := json.NewEncoder(w).Encode(&ErrorsPayload{Errors: errorObjects}); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// ErrorsPayload is a serializer struct for representing a valid JSON API errors payload. | ||
type ErrorsPayload struct { | ||
Errors []*ErrorObject `json:"errors"` | ||
} | ||
|
||
// ErrorObject is an `Error` implementation as well as an implementation of the JSON API error object. | ||
// | ||
// The main idea behind this struct is that you can use it directly in your code as an error type | ||
// and pass it directly to `MarshalErrors` to get a valid JSON API errors payload. | ||
// For more information on Golang errors, see: https://golang.org/pkg/errors/ | ||
// For more information on the JSON API spec's error objects, see: http://jsonapi.org/format/#error-objects | ||
type ErrorObject struct { | ||
// ID is a unique identifier for this particular occurrence of a problem. | ||
ID string `json:"id,omitempty"` | ||
|
||
// Title is a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization. | ||
Title string `json:"title,omitempty"` | ||
|
||
// Detail is a human-readable explanation specific to this occurrence of the problem. Like title, this field’s value can be localized. | ||
Detail string `json:"detail,omitempty"` | ||
|
||
// Status is the HTTP status code applicable to this problem, expressed as a string value. | ||
Status string `json:"status,omitempty"` | ||
|
||
// Code is an application-specific error code, expressed as a string value. | ||
Code string `json:"code,omitempty"` | ||
|
||
// Meta is an object containing non-standard meta-information about the error. | ||
Meta *map[string]interface{} `json:"meta,omitempty"` | ||
} | ||
|
||
// Error implements the `Error` interface. | ||
func (e *ErrorObject) Error() string { | ||
return fmt.Sprintf("Error: %s %s\n", e.Title, e.Detail) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package jsonapi | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestErrorObjectWritesExpectedErrorMessage(t *testing.T) { | ||
err := &ErrorObject{Title: "Title test.", Detail: "Detail test."} | ||
var input error = err | ||
|
||
output := input.Error() | ||
|
||
if output != fmt.Sprintf("Error: %s %s\n", err.Title, err.Detail) { | ||
t.Fatal("Unexpected output.") | ||
} | ||
} | ||
|
||
func TestMarshalErrorsWritesTheExpectedPayload(t *testing.T) { | ||
var marshalErrorsTableTasts = []struct { | ||
In []*ErrorObject | ||
Out map[string]interface{} | ||
}{ | ||
{ // This tests that given fields are turned into the appropriate JSON representation. | ||
In: []*ErrorObject{{ID: "0", Title: "Test title.", Detail: "Test detail", Status: "400", Code: "E1100"}}, | ||
Out: map[string]interface{}{"errors": []interface{}{ | ||
map[string]interface{}{"id": "0", "title": "Test title.", "detail": "Test detail", "status": "400", "code": "E1100"}, | ||
}}, | ||
}, | ||
{ // This tests that the `Meta` field is serialized properly. | ||
In: []*ErrorObject{{Title: "Test title.", Detail: "Test detail", Meta: &map[string]interface{}{"key": "val"}}}, | ||
Out: map[string]interface{}{"errors": []interface{}{ | ||
map[string]interface{}{"title": "Test title.", "detail": "Test detail", "meta": map[string]interface{}{"key": "val"}}, | ||
}}, | ||
}, | ||
} | ||
for _, testRow := range marshalErrorsTableTasts { | ||
buffer, output := bytes.NewBuffer(nil), map[string]interface{}{} | ||
var writer io.Writer = buffer | ||
|
||
_ = MarshalErrors(writer, testRow.In) | ||
json.Unmarshal(buffer.Bytes(), &output) | ||
|
||
if !reflect.DeepEqual(output, testRow.Out) { | ||
t.Fatalf("Expected: \n%#v \nto equal: \n%#v", output, testRow.Out) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.