All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.aws.sdk.kotlin.runtime.protocol.json.RestJsonErrorDeserializer.kt Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */
package aws.sdk.kotlin.runtime.protocol.json

import aws.sdk.kotlin.runtime.InternalSdkApi
import aws.sdk.kotlin.runtime.http.ErrorDetails
import aws.smithy.kotlin.runtime.http.Headers
import aws.smithy.kotlin.runtime.serde.*
import aws.smithy.kotlin.runtime.serde.json.JsonDeserializer
import aws.smithy.kotlin.runtime.serde.json.JsonSerialName

// header identifying the error code
public const val X_AMZN_ERROR_TYPE_HEADER_NAME: String = "X-Amzn-Errortype"

// returned by RESTFUL services that do no send a payload (like in a HEAD request)
public const val X_AMZN_ERROR_MESSAGE_HEADER_NAME: String = "x-amzn-error-message"

// error message header returned by event stream errors
public const val X_AMZN_EVENT_ERROR_MESSAGE_HEADER_NAME: String = ":error-message"

/**
 * Deserializes rest JSON protocol errors as specified by:
 *     - Smithy spec: https://awslabs.github.io/smithy/1.0/spec/aws/aws-restjson1-protocol.html#operation-error-serialization
 *     - SDK Unmarshal Service API Errors (SEP)
 *     - x-amzn-ErrorMessage (SEP)
 */
@InternalSdkApi
public object RestJsonErrorDeserializer {
    // alternative field descriptors for error codes embedded in the document
    private val ERR_CODE_ALT1_DESCRIPTOR = SdkFieldDescriptor(SerialKind.Integer, JsonSerialName("code"))
    private val ERR_CODE_ALT2_DESCRIPTOR = SdkFieldDescriptor(SerialKind.Integer, JsonSerialName("__type"))

    // alternative field descriptors for the error message embedded in the document
    private val MESSAGE_ALT1_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("message"))
    private val MESSAGE_ALT2_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("Message"))
    private val MESSAGE_ALT3_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("errorMessage"))

    private val OBJ_DESCRIPTOR = SdkObjectDescriptor.build {
        field(ERR_CODE_ALT1_DESCRIPTOR)
        field(ERR_CODE_ALT2_DESCRIPTOR)
        field(MESSAGE_ALT1_DESCRIPTOR)
        field(MESSAGE_ALT2_DESCRIPTOR)
        field(MESSAGE_ALT3_DESCRIPTOR)
    }

    public fun deserialize(headers: Headers, payload: ByteArray?): ErrorDetails {
        var code: String? = headers[X_AMZN_ERROR_TYPE_HEADER_NAME]
        var message: String? = headers[X_AMZN_ERROR_MESSAGE_HEADER_NAME]
        if (message == null) {
            message = headers[X_AMZN_EVENT_ERROR_MESSAGE_HEADER_NAME]
        }

        if (payload != null) {
            val deserializer = JsonDeserializer(payload)
            deserializer.deserializeStruct(OBJ_DESCRIPTOR) {
                loop@while (true) {
                    when (findNextFieldIndex()) {
                        ERR_CODE_ALT1_DESCRIPTOR.index,
                        ERR_CODE_ALT2_DESCRIPTOR.index -> code = deserializeString()
                        MESSAGE_ALT1_DESCRIPTOR.index,
                        MESSAGE_ALT2_DESCRIPTOR.index,
                        MESSAGE_ALT3_DESCRIPTOR.index -> message = deserializeString()
                        null -> break@loop
                        else -> skipValue()
                    }
                }
            }
        }

        return ErrorDetails(sanitize(code), message, requestId = null)
    }
}

/**
 * Sanitize the value to retrieve the disambiguated error type using the following steps:
 *
 * 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.
 *
 * All of the following error values resolve to FooError:
 *
 * FooError
 * FooError:http://amazon.com/smithy/com.amazon.smithy.validate/
 * aws.protocoltests.restjson#FooError
 * aws.protocoltests.restjson#FooError:http://amazon.com/smithy/com.amazon.smithy.validate/
 */
private fun sanitize(code: String?): String? = code?.substringAfter("#")?.substringBefore(":")




© 2015 - 2025 Weber Informatics LLC | Privacy Policy