Skip to content

Commit beefc57

Browse files
epauGanesh Jangirganeshnj
authored
feat!: Streaming + Event Streams (#946)
* refactor: streams (#889) smithy-lang/smithy-swift#530 * feat: event stream runtime support (#910) To support event streaming, SDK needs implementations for MessageSigner, MessageEncoder, MessageDecoder, MessageDecoderStream and MessageEncoderStream. - Add AWSMessageEncoder which is a MessageEncoder implementation along with its tests - Add AWSMessageDecoder which is a MessageDecoder implementation along with its tests - Add AWSMessageDecoderStream which is a MessageDecoderStream implementation along with its tests - Add AWSMessageEncoderStream which is a MessageEncoderStream implementation along with its tests - Integrate Payload signer in the SDK which is used in MessageEncoderStream implementation All tests pass. * feat: codegen and integration (#925) * wip * revert to explict header types * comments and fixes for Int/Long * fix test cases * fix test case input * mark * Update Sources/Core/AWSClientRuntime/EventStream/AWSEventStream.swift Co-authored-by: Ed Paulosky <[email protected]> * docs * lint * remove messageDecoder from protocol instead use as local variable * rename method * docs * Update Sources/Core/AWSClientRuntime/HttpContextBuilder+Extension.swift Co-authored-by: Ed Paulosky <[email protected]> * use same pattern * update other places too * remove local deps check * update branch * ALPN change * remove env var * remove ALPN from setup --------- Co-authored-by: Ganesh Jangir <[email protected]> Co-authored-by: Ed Paulosky <[email protected]> * chore: regen models * feat: Forces http2 for bidirectional event streams (#950) * Fixes tests * Fixes codegen test * Some tweaks to tests * Fixes encoder stream tests some times failing * fixes linter warnings * regens models * Post rebase fixes * Adds command to CLI to generate package.swift with integration tests (#965) --------- Co-authored-by: Ganesh Jangir <[email protected]> Co-authored-by: Ganesh Jangir <[email protected]>
1 parent f9be124 commit beefc57

File tree

754 files changed

+50303
-49690
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

754 files changed

+50303
-49690
lines changed

AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/GeneratePackageManifest.swift

+34-5
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@ struct GeneratePackageManifestCommand: ParsableCommand {
3131

3232
@Option(help: "The names of the services to include in the package manifest. This defaults to all services located in aws-sdk-swift/Sources/Services")
3333
var services: [String] = []
34+
35+
@Flag(help: "If the package manifest should include the integration tests.")
36+
var includesIntegrationTests: Bool = false
3437

3538
func run() throws {
3639
let generatePackageManifest = GeneratePackageManifest.standard(
3740
repoPath: repoPath,
3841
packageFileName: packageFileName,
3942
clientRuntimeVersion: clientRuntimeVersion,
4043
crtVersion: crtVersion,
41-
services: services.isEmpty ? nil : services
44+
services: services.isEmpty ? nil : services,
45+
includesIntegrationTests: includesIntegrationTests
4246
)
4347
try generatePackageManifest.run()
4448
}
@@ -61,11 +65,13 @@ struct GeneratePackageManifest {
6165
/// The list of services to include as products
6266
/// If `nil` then the list is populated with the names of all items within the `Sources/Services` directory
6367
let services: [String]?
68+
/// If the package manifest should include the integration tests.
69+
let includesIntegrationTests: Bool
6470

6571
typealias BuildPackageManifest = (
6672
_ clientRuntimeVersion: Version,
6773
_ crtVersion: Version,
68-
_ services: [String]
74+
_ services: [PackageManifestBuilder.Service]
6975
) throws -> String
7076
/// Returns the contents of the package manifest file given the versions of dependencies and the list of services.
7177
let buildPackageManifest: BuildPackageManifest
@@ -85,7 +91,13 @@ struct GeneratePackageManifest {
8591
/// - Returns: The contents of the generated package manifest.
8692
func generatePackageManifestContents() throws -> String {
8793
let versions = try resolveVersions()
88-
let services = try resolveServices()
94+
let servicesWithIntegrationTests = try includesIntegrationTests ? resolveServicesWithIntegrationTests() : []
95+
let services = try resolveServices().map {
96+
PackageManifestBuilder.Service(
97+
name: $0,
98+
includeIntegrationTests: servicesWithIntegrationTests.contains($0)
99+
)
100+
}
89101
log("Creating package manifest contents...")
90102
let contents = try buildPackageManifest(versions.clientRuntime, versions.crt, services)
91103
log("Successfully created package manifest contents")
@@ -170,6 +182,21 @@ struct GeneratePackageManifest {
170182
log("Resolved list of services: \(resolvedServices.count)")
171183
return resolvedServices
172184
}
185+
186+
/// Returns the list of services to include in the package manifest.
187+
/// If an explicit list of services was provided by the command, then this returns the specified services.
188+
/// Otherwise, this returns the list of services that exist within `Sources/Services`
189+
///
190+
/// - Returns: The list of services to include in the package manifest
191+
func resolveServicesWithIntegrationTests() throws -> [String] {
192+
log("Resolving services with integration tests...")
193+
let resolvedServices = try FileManager
194+
.default
195+
.integrationTests()
196+
.map { $0.replacingOccurrences(of: "IntegrationTests", with: "") }
197+
log("List of services with integration tests: \(resolvedServices.count)")
198+
return resolvedServices
199+
}
173200
}
174201

175202
// MARK: - Factory
@@ -191,14 +218,16 @@ extension GeneratePackageManifest {
191218
packageFileName: String,
192219
clientRuntimeVersion: Version? = nil,
193220
crtVersion: Version? = nil,
194-
services: [String]? = nil
221+
services: [String]? = nil,
222+
includesIntegrationTests: Bool = false
195223
) -> Self {
196224
GeneratePackageManifest(
197225
repoPath: repoPath,
198226
packageFileName: packageFileName,
199227
clientRuntimeVersion: clientRuntimeVersion,
200228
crtVersion: crtVersion,
201-
services: services
229+
services: services,
230+
includesIntegrationTests: includesIntegrationTests
202231
) { _clientRuntimeVersion, _crtVersion, _services in
203232
let builder = PackageManifestBuilder(
204233
clientRuntimeVersion: _clientRuntimeVersion,

AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Models/PackageManifestBuilder.swift

+40-5
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@ import PackageDescription
1010

1111
/// Builds the contents of the package manifest file.
1212
struct PackageManifestBuilder {
13+
struct Service {
14+
let name: String
15+
let includeIntegrationTests: Bool
16+
}
17+
1318
let clientRuntimeVersion: Version
1419
let crtVersion: Version
15-
let services: [String]
20+
let services: [Service]
1621
let basePackageContents: () throws -> String
1722

1823
init(
1924
clientRuntimeVersion: Version,
2025
crtVersion: Version,
21-
services: [String],
26+
services: [Service],
2227
basePackageContents: @escaping () throws -> String
2328
) {
2429
self.clientRuntimeVersion = clientRuntimeVersion
@@ -30,7 +35,7 @@ struct PackageManifestBuilder {
3035
init(
3136
clientRuntimeVersion: Version,
3237
crtVersion: Version,
33-
services: [String]
38+
services: [Service]
3439
) {
3540
self.init(clientRuntimeVersion: clientRuntimeVersion, crtVersion: crtVersion, services: services) {
3641
// Returns the contents of the base package manifest stored in the bundle at `Resources/Package.Base.swift`
@@ -75,7 +80,10 @@ struct PackageManifestBuilder {
7580
buildDependencies(),
7681
"",
7782
// Add the generated content that defines the list of services to include
78-
buildServiceTargets()
83+
buildServiceTargets(),
84+
"",
85+
// Add the generated content that defines the list of services with integration tests to include
86+
buildIntegrationTestsTargets()
7987
]
8088
return contents.joined(separator: .newline)
8189
}
@@ -125,11 +133,38 @@ struct PackageManifestBuilder {
125133

126134
var lines: [String] = []
127135
lines += ["let \(propertyName): [String] = ["]
128-
lines += services.map { " \($0.wrappedInQuotes())," }
136+
lines += services.map { " \($0.name.wrappedInQuotes())," }
129137
lines += ["]"]
130138
lines += [""]
131139
lines += ["\(propertyName).forEach(addServiceTarget)"]
132140

133141
return lines.joined(separator: .newline)
134142
}
143+
144+
/// Builds the list of services to add integration test targets for..
145+
/// This generates an array of strings, where the each item is a name of a service
146+
/// and calls the `addIntegrationTestTarget` for each item.
147+
///
148+
///```
149+
///let servicesWithIntegrationTests: [String] = [
150+
/// "Service Name 1",
151+
/// "Service Name 2",
152+
/// "Service Name 3"
153+
/// // etc...
154+
///]
155+
///serviceTagets.forEach(addIntegrationTestTarget)
156+
///```
157+
///
158+
private func buildIntegrationTestsTargets() -> String {
159+
let propertyName = "servicesWithIntegrationTests"
160+
161+
var lines: [String] = []
162+
lines += ["let \(propertyName): [String] = ["]
163+
lines += services.filter(\.includeIntegrationTests).map { " \($0.name.wrappedInQuotes())," }
164+
lines += ["]"]
165+
lines += [""]
166+
lines += ["\(propertyName).forEach(addIntegrationTestTarget)"]
167+
168+
return lines.joined(separator: .newline)
169+
}
135170
}

AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/Package.Base.swift

+12
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,15 @@ func addServiceTarget(_ name: String) {
103103
)
104104
]
105105
}
106+
107+
func addIntegrationTestTarget(_ name: String) {
108+
let integrationTestName = "\(name)IntegrationTests"
109+
package.targets += [
110+
.testTarget(
111+
name: integrationTestName,
112+
dependencies: [.crt, .clientRuntime, .awsClientRuntime, .byName(name: name), .smithyTestUtils],
113+
path: "./IntegrationTests/Services/\(integrationTestName)",
114+
resources: [.process("Resources")]
115+
)
116+
]
117+
}

AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Utils/FileManager+Utils.swift

+10
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,14 @@ extension FileManager {
3535
.sorted()
3636
.filter { !$0.hasPrefix(".") }
3737
}
38+
39+
/// Returns the list of integration tests.
40+
///
41+
/// - Returns: The list of integration tests.
42+
func integrationTests() throws -> [String] {
43+
try FileManager.default
44+
.contentsOfDirectory(atPath: "IntegrationTests/Services")
45+
.sorted()
46+
.filter { !$0.hasPrefix(".") }
47+
}
3848
}

AWSSDKSwiftCLI/Tests/AWSSDKSwiftCLITests/Commands/GeneratePackageManifestTests.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class GeneratePackageManifestTests: CLITestCase {
4848
createServiceFolders(services)
4949

5050
let subject = GeneratePackageManifest.mock(buildPackageManifest: { _clientRuntimeVersion, _crtVersion, services in
51-
"\(_clientRuntimeVersion)-\(_crtVersion)-\(services.joined(separator: "-"))"
51+
"\(_clientRuntimeVersion)-\(_crtVersion)-\(services.map(\.name).joined(separator: "-"))"
5252
})
5353
try! subject.run()
5454
let result = try! String(contentsOfFile: "Package.swift", encoding: .utf8)
@@ -110,6 +110,7 @@ extension GeneratePackageManifest {
110110
clientRuntimeVersion: Version? = nil,
111111
crtVersion: Version? = nil,
112112
services: [String]? = nil,
113+
includesIntegrationTests: Bool = false,
113114
buildPackageManifest: @escaping BuildPackageManifest = { (_,_,_) throws -> String in "" }
114115
) -> GeneratePackageManifest {
115116
GeneratePackageManifest(
@@ -118,6 +119,7 @@ extension GeneratePackageManifest {
118119
clientRuntimeVersion: clientRuntimeVersion,
119120
crtVersion: crtVersion,
120121
services: services,
122+
includesIntegrationTests: includesIntegrationTests,
121123
buildPackageManifest: buildPackageManifest
122124
)
123125
}

AWSSDKSwiftCLI/Tests/AWSSDKSwiftCLITests/Commands/PrepareReleaseTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class PrepareReleaseTests: CLITestCase {
104104
ProcessRunner.testRunner = runner
105105
let subject = PrepareRelease.mock(repoType: .awsSdkSwift)
106106
try! subject.stageFiles()
107-
XCTAssertTrue(command.hasSuffix("git add Package.swift Package.version Sources/Services Tests/Services"))
107+
XCTAssertTrue(command.hasSuffix("git add Package.swift Package.version packageDependencies.plist Sources/Services Tests/Services"))
108108
}
109109

110110
func testStageFilesForSmithySwift() {

AWSSDKSwiftCLI/Tests/AWSSDKSwiftCLITests/Models/PackageManifestBuilderTests.swift

+11-1
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,23 @@ class PackageManifestBuilderTests: XCTestCase {
2929
]
3030
3131
serviceTargets.forEach(addServiceTarget)
32+
33+
let servicesWithIntegrationTests: [String] = [
34+
"A",
35+
"B",
36+
"C",
37+
"D",
38+
"E",
39+
]
40+
41+
servicesWithIntegrationTests.forEach(addIntegrationTestTarget)
3242
"""
3343

3444
func testBuild() {
3545
let subjeect = PackageManifestBuilder(
3646
clientRuntimeVersion: .init("1.2.3"),
3747
crtVersion: .init("4.5.6"),
38-
services: ["A","B","C","D","E"],
48+
services: ["A","B","C","D","E"].map { PackageManifestBuilder.Service(name: $0, includeIntegrationTests: true) },
3949
basePackageContents: { "<contents of base package>" }
4050
)
4151
let result = try! subjeect.build()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello, world!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import XCTest
9+
import AWSS3
10+
11+
final class S3Tests: XCTestCase {
12+
var sut: S3Client!
13+
let bucketName = "aws-sdk-swift-test-\(UUID().uuidString.split(separator: "-").first!.lowercased())"
14+
let objectName = "hello-world"
15+
let expected = "Hello, world!"
16+
17+
override func setUp() async throws {
18+
sut = try S3Client(region: "us-west-2")
19+
try await createBucket()
20+
}
21+
22+
override func tearDown() async throws {
23+
try await emptyBucket()
24+
try await deleteBucket()
25+
}
26+
27+
func testGetObject() async throws {
28+
try await putObject()
29+
let input = GetObjectInput(bucket: bucketName, key: objectName)
30+
let output = try await sut.getObject(input: input)
31+
XCTAssertNotNil(output)
32+
XCTAssertNotNil(output.body)
33+
34+
switch output.body! {
35+
case .data(let data):
36+
let actual = String(data: data!, encoding: .utf8)
37+
XCTAssertEqual(actual, expected)
38+
case .stream(let stream):
39+
let actual = String(data: try stream.readToEnd()!, encoding: .utf8)
40+
print(actual)
41+
XCTAssertEqual(actual, expected)
42+
}
43+
}
44+
45+
func testPutObject_givenDataBody() async throws {
46+
let input = PutObjectInput(body: .data(expected.data(using: .utf8)), bucket: bucketName, key: objectName)
47+
let output = try await sut.putObject(input: input)
48+
XCTAssertNotNil(output)
49+
50+
let actual = try await getObject()
51+
XCTAssertEqual(expected, actual)
52+
}
53+
54+
func testPutObject_givenStreamBody() async throws {
55+
let audioURL = Bundle.module.url(forResource: "hello-world", withExtension: nil)!
56+
let fileHandle = FileHandle(forReadingAtPath: audioURL.relativePath)!
57+
let input = PutObjectInput(body: .from(fileHandle: fileHandle), bucket: bucketName, key: objectName)
58+
let output = try await sut.putObject(input: input)
59+
XCTAssertNotNil(output)
60+
61+
let actual = try await getObject()
62+
XCTAssertEqual(expected, actual)
63+
}
64+
65+
// MARK: Helpers
66+
67+
private func createBucket() async throws {
68+
let input = CreateBucketInput(bucket: bucketName, createBucketConfiguration: S3ClientTypes.CreateBucketConfiguration(locationConstraint: S3ClientTypes.BucketLocationConstraint.usWest2))
69+
let output = try await sut.createBucket(input: input)
70+
print(output)
71+
}
72+
73+
private func getObject() async throws -> String? {
74+
let input = GetObjectInput(bucket: bucketName, key: objectName)
75+
let output = try await sut.getObject(input: input)
76+
XCTAssertNotNil(output)
77+
XCTAssertNotNil(output.body)
78+
79+
switch output.body! {
80+
case .data(let data):
81+
return String(data: data!, encoding: .utf8)
82+
case .stream(let stream):
83+
return String(data: try stream.readToEnd()!, encoding: .utf8)
84+
}
85+
}
86+
87+
private func deleteBucket() async throws {
88+
let input = DeleteBucketInput(bucket: bucketName)
89+
let output = try await sut.deleteBucket(input: input)
90+
print(output)
91+
}
92+
93+
private func emptyBucket() async throws {
94+
let input = ListObjectsV2Input(bucket: bucketName)
95+
let output = try await sut.listObjectsV2(input: input)
96+
let objects = output.contents ?? []
97+
print("Deleting \(objects.count) objects in \(bucketName)")
98+
for object in objects {
99+
print("Deleting \(String(describing: object.key)) in \(bucketName)")
100+
let deleteInput = DeleteObjectInput(bucket: bucketName, key: object.key)
101+
let deleteOutput = try await sut.deleteObject(input: deleteInput)
102+
print(deleteOutput)
103+
}
104+
}
105+
106+
private func putObject() async throws {
107+
let input = PutObjectInput(body: .data(expected.data(using: .utf8)), bucket: bucketName, key: objectName)
108+
let output = try await sut.putObject(input: input)
109+
print(output)
110+
}
111+
112+
}

0 commit comments

Comments
 (0)