Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds RestJson deserializer and middleware generation support #568

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions aws/protocol/json/decoder_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package json

import (
"encoding/json"
"fmt"
"github.com/awslabs/smithy-go"
"io"
"strings"
)

// GetErrorInfo util looks for code, __type, and message members in the
// json body. These members are optionally available, and the function
// returns the value of member if it is available. This function is useful to
// identify the error code, msg in a REST JSON error response.
func GetErrorInfo(decoder *json.Decoder) (errorType string, message string, err error) {
startToken, err := decoder.Token()
if err == io.EOF {
return "", "", nil
}
if err != nil {
return "", "", err
}

if t, ok := startToken.(json.Delim); !ok || t.String() != "{" {
return "", "", fmt.Errorf("expected start token to be {")
}

for decoder.More() {
var target *string
t, err := decoder.Token()
if err != nil {
return "", "", err
}

switch st := t.(string); {
case strings.EqualFold(st, "code"):
case strings.EqualFold(st, "__type"):
target = &errorType
case strings.EqualFold(st, "message"):
target = &message
skotambkar marked this conversation as resolved.
Show resolved Hide resolved
default:
DiscardUnknownField(decoder)
continue
}

v, err := decoder.Token()
if err != nil {
return errorType, message, err
}
*target = v.(string)
}

endToken, err := decoder.Token()
if err != nil {
return "", "", err
}

if t, ok := endToken.(json.Delim); !ok || t.String() != "}" {
return "", "", fmt.Errorf("expected end token to be }")
}
return errorType, message, nil
}

// SanitizeErrorCode sanitizes the errorCode string .
// The rule for sanitizing is if a `:` character is present, then take only the
// contents before the first : character in the value.
// If a # character is present, then take only the contents after the
// first # character in the value.
func SanitizeErrorCode(errorCode string) string {
skotambkar marked this conversation as resolved.
Show resolved Hide resolved
if strings.ContainsAny(errorCode, ":") {
errorCode = strings.SplitN(errorCode, ":", 2)[0]
}

if strings.ContainsAny(errorCode, "#") {
errorCode = strings.SplitN(errorCode, "#", 2)[1]
}

return errorCode
}

// DiscardUnknownField discards unknown fields from decoder body.
// This function is useful while deserializing json body with additional
// unknown information that should be discarded.
func DiscardUnknownField(decoder *json.Decoder) error {
v, err := decoder.Token()
if err == io.EOF {
return nil
}
if err != nil {
return err
}

if _, ok := v.(json.Delim); ok {
for decoder.More() {
err = DiscardUnknownField(decoder)
}
endToken, err := decoder.Token()
if err != nil {
return err
}
if _, ok := endToken.(json.Delim); !ok {
return fmt.Errorf("invalid JSON : expected json delimiter, found %T %v",
endToken, endToken)
}
}

return nil
}

// GetSmithyGenericAPIError returns smithy generic api error and an error interface.
// Takes in json decoder, and error Code string as args. The function retrieves error message
// and error code from the decoder body. If errorCode of length greater than 0 is passed in as
// an argument, it is used instead.
func GetSmithyGenericAPIError(decoder *json.Decoder, errorCode string) (smithy.GenericAPIError, error) {
skotambkar marked this conversation as resolved.
Show resolved Hide resolved
errorType, message, err := GetErrorInfo(decoder)
if err != nil {
return smithy.GenericAPIError{}, err
}

if len(errorCode) == 0 {
errorCode = SanitizeErrorCode(errorType)
}
skotambkar marked this conversation as resolved.
Show resolved Hide resolved

return smithy.GenericAPIError{
Code: errorCode,
Message: message,
}, nil
}
Loading