Skip to content

Commit

Permalink
Merge branch 'main' into fix/generate-and-include-smithy-module-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
sichanyoo authored Aug 7, 2024
2 parents f0b212f + e5fd31d commit b28c5f6
Show file tree
Hide file tree
Showing 22 changed files with 859 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,18 @@ import AwsCommonRuntimeKit
import ClientRuntime

public class MockHttpClientEngine: HTTPClient {
private let errorResponsePayload: String

// Public initializer
public init() {}
public init(response: String) {
self.errorResponsePayload = response
}

func successHttpResponse(request: SmithyHTTPAPI.HTTPRequest) -> HTTPResponse {
let errorResponsePayload = """
<Error>
<Code>SlowDown</Code>
<Message>Please reduce your request rate.</Message>
<RequestId>K2H6N7ZGQT6WHCEG</RequestId>
<HostId>WWoZlnK4pTjKCYn6eNV7GgOurabfqLkjbSyqTvDMGBaI9uwzyNhSaDhOCPs8paFGye7S6b/AB3A=</HostId>
</Error>
"""
request.withHeader(name: "Date", value: "Wed, 21 Oct 2015 07:28:00 GMT")
return HTTPResponse(
headers: request.headers,
body: ByteStream.data(errorResponsePayload.data(using: .utf8)),
body: ByteStream.data(self.errorResponsePayload.data(using: .utf8)),
statusCode: .ok
)
}
Expand All @@ -44,25 +39,101 @@ public class MockHttpClientEngine: HTTPClient {

class S3ErrorIn200Test: XCTestCase {

let errorInternalErrorResponsePayload = """
<Error>
<Code>InternalError</Code>
<Message>We encountered an internal error. Please try again.</Message>
<RequestId>656c76696e6727732072657175657374</RequestId>
<HostId>Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==</HostId>
</Error>
"""

let errorSlowDownResponsePayload = """
<Error>
<Code>SlowDown</Code>
<Message>Please reduce your request rate.</Message>
<RequestId>K2H6N7ZGQT6WHCEG</RequestId>
<HostId>WWoZlnK4pTjKCYn6eNV7GgOurabfqLkjbSyqTvDMGBaI9uwzyNhSaDhOCPs8paFGye7S6b/AB3A=</HostId>
</Error>
"""

let shouldNotApplyResponsePayload = """
<DeleteResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Deleted>
<Key>sample1.txt</Key>
</Deleted>
<Error>
<Key>sample2.txt</Key>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
</Error>
</DeleteResult>
"""

override class func setUp() {
AwsCommonRuntimeKit.CommonRuntimeKit.initialize()
}

/// S3Client throws expected error in response (200) with <Error> tag
func test_foundExpectedError() async throws {
/// S3Client throws expected InternalError error in response (200) with <Error> tag
func test_foundInternalErrorExpectedError() async throws {
let config = try await S3Client.S3ClientConfiguration(region: "us-west-2")
config.httpClientEngine = MockHttpClientEngine()
config.httpClientEngine = MockHttpClientEngine(response: errorInternalErrorResponsePayload)
let client = S3Client(config: config)

do {
// any method on S3Client where the output shape doesnt have a stream
// any method on S3Client where the output shape doesnt have a blob stream
_ = try await client.listBuckets(input: .init())
XCTFail("Expected an error to be thrown, but it was not.")
} catch let error as UnknownAWSHTTPServiceError {
// check for the error we added in our mock client
XCTAssertEqual("InternalError", error.typeName)
XCTAssertEqual("We encountered an internal error. Please try again.", error.message)
} catch {
XCTFail("Unexpected error: \(error)")
}
}

/// S3Client throws expected SlowDown error in response (200) with <Error> tag
func test_foundSlowDownExpectedError() async throws {
let config = try await S3Client.S3ClientConfiguration(region: "us-west-2")
config.httpClientEngine = MockHttpClientEngine(response: errorSlowDownResponsePayload)
let client = S3Client(config: config)

do {
// any method on S3Client where the output shape doesnt have a blob stream
_ = try await client.listBuckets(input: .init())
XCTFail("Expected an error to be thrown, but it was not.")
} catch let error as UnknownAWSHTTPServiceError {
// check for the error we added in our mock client
XCTAssertEqual("SlowDown", error.typeName)
XCTAssertEqual("Please reduce your request rate.", error.message)
} catch {
XCTFail("Unexpected error: \(error)")
}
}

/// S3Client does not throw error when <Error> is not at the root
func test_noErrorExpected() async throws {
let config = try await S3Client.S3ClientConfiguration(region: "us-west-2")
config.httpClientEngine = MockHttpClientEngine(response: shouldNotApplyResponsePayload)
let client = S3Client(config: config)

do {
// any method on S3Client where the output shape doesnt have a stream
let result = try await client.deleteObjects(input: .init(delete: .init(objects: [.init(key: "test")])))

// Check results
XCTAssertEqual(result.deleted?.count, 1)
XCTAssertEqual(result.errors?.count, 1)

let actualDeleted = result.deleted?.first
XCTAssertEqual(actualDeleted?.key, "sample1.txt")

let actualError = result.errors?.first
XCTAssertEqual(actualError?.code, "AccessDenied")
XCTAssertEqual(actualError?.key, "sample2.txt")
} catch let error {
XCTFail("Expected success, but received \(error).")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ class S3EventStreamTests: S3XCTestCase {
outputSerialization: S3ClientTypes.OutputSerialization(json: S3ClientTypes.JSONOutput())
))

let outputStream = result.payload
guard let outputStream = result.payload else {
XCTFail("result.payload is nil")
return
}

var actualOutput = ""

for try await event in outputStream! {
for try await event in outputStream {
switch event {
case .records(let record):
actualOutput = actualOutput + (String(data: record.payload ?? Data(), encoding: .utf8) ?? "")
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ func addResolvedTargets() {
// MARK: - Generated

addDependencies(
clientRuntimeVersion: "0.54.0",
clientRuntimeVersion: "0.55.0",
crtVersion: "0.32.0"
)

Expand Down
2 changes: 1 addition & 1 deletion Package.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.51.0
0.52.0
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,25 @@
// SPDX-License-Identifier: Apache-2.0
//

import enum Smithy.ByteStream
import class Smithy.Context
import ClientRuntime
import SmithyHTTPAPI
import SmithyXML
import struct Foundation.Data
import SmithyStreams

public struct AWSS3ErrorWith200StatusXMLMiddleware<OperationStackInput, OperationStackOutput> {
public let id: String = "AWSS3ErrorWith200StatusXMLMiddleware"
private let errorStatusCode: HTTPStatusCode = .internalServerError

public init() {}

private func isErrorWith200Status(response: HTTPResponse) async throws -> Bool {
// Check if the status code is OK (200)
guard response.statusCode == .ok else {
return false
}

// Check if the response body contains an XML Error
guard let data = try await response.body.readData() else {
return false
}
private func isRootErrorElement(data: Data) throws -> Bool {
let reader = try Reader.from(data: data)

response.body = .data(data)
let xmlString = String(decoding: data, as: UTF8.self)
return xmlString.contains("<Error>")
// Check if there's an "Error" node at the root of the XML response
return reader.nodeInfo.name == "Error"
}
}

Expand All @@ -40,9 +35,41 @@ extension AWSS3ErrorWith200StatusXMLMiddleware: HttpInterceptor {
context: some MutableResponse<Self.InputType, Self.RequestType, Self.ResponseType>
) async throws {
let response = context.getResponse()
if try await isErrorWith200Status(response: response) {
response.statusCode = errorStatusCode
context.updateResponse(updated: response)

// Check if the status code is OK (200)
guard response.statusCode == .ok else {
return
}

guard let data = try await response.body.readData() else {
return
}

let statusCode = try isRootErrorElement(data: data) ? errorStatusCode : response.statusCode

// For event streams the body needs to be copied as buffered streams are non-seekable
let updatedBody = response.body.copy(data: data)

let updatedResponse = response.copy(
body: updatedBody,
statusCode: statusCode
)

context.updateResponse(updated: updatedResponse)
}
}

extension ByteStream {

// Copy an existing ByteStream, optionally with new data
public func copy(data: Data?) -> ByteStream {
switch self {
case .data(let existingData):
return .data(data ?? existingData)
case .stream(let existingStream):
return .stream(data != nil ? BufferedStream(data: data, isClosed: true) : existingStream)
case .noStream:
return .noStream
}
}
}
17 changes: 16 additions & 1 deletion Sources/Core/AWSSDKForSwift/Documentation.docc/AWSSDKForSwift.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,23 @@ A pure-Swift SDK for accessing all published AWS services.

This SDK is open-source. Code is available on Github [here](https://github.com/awslabs/aws-sdk-swift).

## Service Documentation

## AWS Runtime Module Documentation

[AWSClientRuntime](../../../../../swift/api/awsclientruntime/latest)

[AWSSDKChecksums](../../../../../swift/api/awssdkchecksums/latest)

[AWSSDKCommon](../../../../../swift/api/awssdkcommon/latest)

[AWSSDKEventStreamsAuth](../../../../../swift/api/awssdkeventstreamsauth/latest)

[AWSSDKHTTPAuth](../../../../../swift/api/awssdkhttpauth/latest)

[AWSSDKIdentity](../../../../../swift/api/awssdkidentity/latest)


## Service Documentation

[AWSACM](../../../../../swift/api/awsacm/latest)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ extension BedrockAgentRuntimeClient {

/// Performs the `InvokeFlow` operation on the `AmazonBedrockAgentRunTimeService` service.
///
/// Invokes an alias of a flow to run the inputs that you specify and return the output of each node as a stream. If there's an error, the error is returned. For more information, see [Test a flow in Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/flows-test.html) in the Amazon Bedrock User Guide.
/// Invokes an alias of a flow to run the inputs that you specify and return the output of each node as a stream. If there's an error, the error is returned. For more information, see [Test a flow in Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/flows-test.html) in the Amazon Bedrock User Guide. The CLI doesn't support streaming operations in Amazon Bedrock, including InvokeFlow.
///
/// - Parameter InvokeFlowInput : [no documentation found]
///
Expand Down
Loading

0 comments on commit b28c5f6

Please sign in to comment.