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

commonMain.aws.smithy.kotlin.runtime.awsprotocol.xml.RestXmlErrorDeserializer.kt Maven / Gradle / Ivy

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

import aws.smithy.kotlin.runtime.InternalApi
import aws.smithy.kotlin.runtime.awsprotocol.ErrorDetails
import aws.smithy.kotlin.runtime.serde.*
import aws.smithy.kotlin.runtime.serde.xml.XmlTagReader
import aws.smithy.kotlin.runtime.serde.xml.data
import aws.smithy.kotlin.runtime.serde.xml.xmlTagReader

/**
 * Provides access to specific values regardless of message form
 */
internal interface RestXmlErrorDetails {
    val requestId: String?
    val code: String?
    val message: String?
}

internal data class XmlError(
    override val requestId: String?,
    override val code: String?,
    override val message: String?,
) : RestXmlErrorDetails

/**
 * Deserializes rest XML protocol errors as specified by:
 * https://awslabs.github.io/smithy/1.0/spec/aws/aws-restxml-protocol.html#error-response-serialization
 *
 * Returns parsed data in normalized form or throws [DeserializationException] if response cannot be parsed.
 */
@Deprecated("use parseRestXmlErrorResponseNoSuspend")
@InternalApi
public suspend fun parseRestXmlErrorResponse(payload: ByteArray): ErrorDetails =
    parseRestXmlErrorResponseNoSuspend(payload)

public fun parseRestXmlErrorResponseNoSuspend(payload: ByteArray): ErrorDetails {
    val details = XmlErrorDeserializer.deserialize(xmlTagReader(payload))
    return ErrorDetails(details.code, details.message, details.requestId)
}

/**
 * This deserializer is used for both wrapped and unwrapped restXml errors.
 */
internal object XmlErrorDeserializer {
    fun deserialize(root: XmlTagReader): XmlError = runCatching {
        var message: String? = null
        var code: String? = null
        var requestId: String? = null

        val rootTagName = root.tagName
        check(rootTagName == "ErrorResponse" || rootTagName == "Error") {
            "expected restXml error response with root tag of  or "
        }

        // wrapped error, unwrap it
        var errTag = root
        if (root.tagName == "ErrorResponse") {
            errTag = root.nextTag() ?: error("expected more tags after ")
        }

        if (errTag.tagName == "Error") {
            loop@while (true) {
                val curr = errTag.nextTag() ?: break@loop
                when (curr.tagName) {
                    "Code" -> code = curr.data()
                    "Message", "message" -> message = curr.data()
                    "RequestId" -> requestId = curr.data()
                }
                curr.drop()
            }
        }

        // wrapped responses
        if (requestId == null) {
            loop@while (true) {
                val curr = root.nextTag() ?: break@loop
                when (curr.tagName) {
                    "RequestId" -> requestId = curr.data()
                }
            }
        }

        XmlError(requestId, code, message)
    }.getOrDeserializeErr { "Unable to deserialize RestXml error" }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy