override fun renderThrowOperationError()

in codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/customization/s3/S3Generator.kt [42:120]


    override fun renderThrowOperationError(
        ctx: ProtocolGenerator.GenerationContext,
        op: OperationShape,
        writer: KotlinWriter
    ) {
        val exceptionBaseSymbol = ExceptionBaseClassGenerator.baseExceptionSymbol(ctx.settings)

        val setS3ErrorMetadata = buildSymbol {
            name = "setS3ErrorMetadata"
            namespace = "${ctx.settings.pkg.name}.internal"
        }

        val parseS3ErrorResponse = buildSymbol {
            name = "parseS3ErrorResponse"
            namespace = "${ctx.settings.pkg.name}.internal"
        }

        val s3ErrorDetails = buildSymbol {
            name = "S3ErrorDetails"
            namespace = "${ctx.settings.pkg.name}.internal"
        }

        listOf(
            exceptionBaseSymbol,
            RuntimeTypes.Http.readAll,
            RuntimeTypes.Http.StatusCode,
            AwsRuntimeTypes.Http.withPayload,
            s3ErrorDetails,
            setS3ErrorMetadata,
            parseS3ErrorResponse,
        ).forEach(writer::addImport)

        writer.write("""val payload = response.body.readAll()""")
            .write("val wrappedResponse = response.withPayload(payload)")
            .write("")
            .write("val errorDetails = try {")
            .indent()
            .call {
                // customize error matching to handle HeadObject/HeadBucket error responses which have no payload
                writer.write("if (payload == null && response.status == HttpStatusCode.NotFound) {")
                    .indent()
                    .write("""S3ErrorDetails(code = "NotFound")""")
                    .dedent()
                    .write("} else {")
                    .indent()
                    .write("""checkNotNull(payload){ "unable to parse error from empty response" }""")
                    .write("#T(payload)", parseS3ErrorResponse)
                    .dedent()
                    .write("}")
            }
            .dedent()
            .withBlock("} catch (ex: Exception) {", "}") {
                withBlock("""throw #T("Failed to parse response as '${ctx.protocol.name}' error", ex).also {""", "}", exceptionBaseSymbol) {
                    write("#T(it, wrappedResponse, null)", setS3ErrorMetadata)
                }
            }
            .write("")

        if (op.errors.isEmpty()) {
            writer.write("throw #T(errorDetails.message)", exceptionBaseSymbol)
        } else {
            writer.openBlock("val modeledExceptionDeserializer = when(errorDetails.code) {", "}") {
                op.errors.forEach { err ->
                    val errSymbol = ctx.symbolProvider.toSymbol(ctx.model.expectShape(err))
                    val errDeserializerSymbol = buildSymbol {
                        name = "${errSymbol.name}Deserializer"
                        namespace = "${ctx.settings.pkg.name}.transform"
                    }
                    writer.write("#S -> #T()", getErrorCode(ctx, err), errDeserializerSymbol)
                }
                writer.write("else -> throw #T(errorDetails.message)", exceptionBaseSymbol)
            }

            writer.write("")
                .write("val modeledException = modeledExceptionDeserializer.deserialize(context, wrappedResponse)")
                .write("#T(modeledException, wrappedResponse, errorDetails)", setS3ErrorMetadata)
                .write("throw modeledException")
        }
    }