go/protocol/internal/errutil/protocol.go (223 lines of code) (raw):

// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. package errutil import ( "fmt" "strconv" "github.com/Azure/iot-operations-sdks/go/protocol/errors" "github.com/Azure/iot-operations-sdks/go/protocol/internal/constants" "github.com/Azure/iot-operations-sdks/go/protocol/internal/version" "github.com/sosodev/duration" ) type result struct { status int message string application bool name string value any version string supportedVersions []int } func ToUserProp(err error) map[string]string { if err == nil { return (&result{status: 200}).props() } var message string var kind errors.Kind switch e := err.(type) { case *errors.Client: message = e.Message kind = e.Kind case *errors.Remote: message = e.Message kind = e.Kind default: return (&result{ status: 500, message: "invalid error", }).props() } switch k := kind.(type) { case errors.HeaderMissing: return (&result{ status: 400, message: message, name: k.HeaderName, }).props() case errors.HeaderInvalid: if k.HeaderName == constants.ContentType || k.HeaderName == constants.FormatIndicator { return (&result{ status: 415, message: message, name: k.HeaderName, value: k.HeaderValue, }).props() } return (&result{ status: 400, message: message, name: k.HeaderName, value: k.HeaderValue, }).props() case errors.PayloadInvalid: return (&result{ status: 400, message: message, }).props() case errors.Timeout: return (&result{ status: 408, message: message, name: k.TimeoutName, value: duration.Format(k.TimeoutValue), }).props() case errors.StateInvalid: return (&result{ status: 503, message: message, name: k.PropertyName, }).props() case errors.InternalLogicError: return (&result{ status: 500, message: message, //nolint:staticcheck // Capture for wire protocol compat. name: k.PropertyName, }).props() case errors.UnknownError: return (&result{ status: 500, message: message, }).props() case errors.ExecutionError: return (&result{ status: 500, message: message, application: true, }).props() case errors.UnsupportedVersion: return (&result{ status: 505, message: message, version: k.ProtocolVersion, supportedVersions: k.SupportedMajorProtocolVersions, }).props() default: return (&result{ status: 500, message: "invalid error kind", name: "Kind", }).props() } } func FromUserProp(user map[string]string) error { status := user[constants.Status] statusMessage := user[constants.StatusMessage] propertyName := user[constants.InvalidPropertyName] propertyValue := user[constants.InvalidPropertyValue] protocolVersion := user[constants.RequestProtocolVersion] supportedVersions := user[constants.SupportedProtocolMajorVersion] if status == "" { return &errors.Client{ Message: "status missing", Kind: errors.HeaderMissing{ HeaderName: constants.Status, }, } } code, err := strconv.ParseInt(status, 10, 32) if err != nil { return &errors.Client{ Message: "status is not a valid integer", Kind: errors.HeaderInvalid{ HeaderName: constants.Status, HeaderValue: status, }, Nested: err, } } // No error, we're done. if code < 400 { return nil } e := &errors.Remote{Message: statusMessage} switch code { case 400, 415: switch { case propertyName == "" && propertyValue == "": e.Kind = errors.PayloadInvalid{} case propertyValue == "": e.Kind = errors.HeaderMissing{ HeaderName: propertyName, } default: e.Kind = errors.HeaderInvalid{ HeaderName: propertyName, HeaderValue: propertyValue, } } case 408: to, err := duration.Parse(propertyValue) if err != nil { return &errors.Client{ Message: "invalid timeout value", Kind: errors.HeaderInvalid{ HeaderName: constants.InvalidPropertyValue, HeaderValue: propertyValue, }, Nested: err, } } e.Kind = errors.Timeout{ TimeoutName: propertyName, TimeoutValue: to.ToTimeDuration(), } case 500: appErr := user[constants.IsApplicationError] switch { case appErr != "" && appErr != "false": e.Kind = errors.ExecutionError{} case propertyName != "": e.Kind = errors.InternalLogicError{PropertyName: propertyName} default: e.Kind = errors.UnknownError{} } case 503: e.Kind = errors.StateInvalid{ PropertyName: propertyName, } case 505: e.Kind = errors.UnsupportedVersion{ ProtocolVersion: protocolVersion, SupportedMajorProtocolVersions: version.ParseSupported( supportedVersions, ), } default: // Treat unknown status as an unknown error, but otherwise allow them. k := errors.UnknownError{} //nolint:staticcheck // Capture 422 data for schemaregistry. k.PropertyName = propertyName if propertyValue != "" { //nolint:staticcheck // Capture 422 data for schemaregistry. k.PropertyValue = propertyValue } e.Kind = k } return e } func (r *result) props() map[string]string { props := make(map[string]string, 5) props[constants.Status] = fmt.Sprint(r.status) props[constants.StatusMessage] = r.message if r.application { props[constants.IsApplicationError] = "true" } if r.name != "" { props[constants.InvalidPropertyName] = r.name if r.value != nil { props[constants.InvalidPropertyValue] = fmt.Sprint(r.value) } } if r.version != "" { props[constants.RequestProtocolVersion] = r.version props[constants.SupportedProtocolMajorVersion] = version.SerializeSupported( r.supportedVersions, ) } return props }