Skip to content

Commit c2ab0a5

Browse files
sichanyooSichan Yoo
andauthored
feat: add utility method for converting SdkHttpRequest to URLRequest. (#613)
* Add extension constructor to URLRequest to convert SDKHttpRequest * Add preprocessor conditional import functionality to SwiftWriter. --------- Co-authored-by: Sichan Yoo <[email protected]>
1 parent c19f585 commit c2ab0a5

File tree

4 files changed

+90
-5
lines changed

4 files changed

+90
-5
lines changed

Sources/ClientRuntime/Networking/Http/SdkHttpRequest.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ import struct Foundation.CharacterSet
66
import struct Foundation.URLQueryItem
77
import struct Foundation.URLComponents
88
import AwsCommonRuntimeKit
9+
// In Linux, Foundation.URLRequest is moved to FoundationNetworking.
10+
#if canImport(FoundationNetworking)
11+
import FoundationNetworking
12+
#else
13+
import struct Foundation.URLRequest
14+
#endif
915

1016
// we need to maintain a reference to this same request while we add headers
1117
// in the CRT engine so that is why it's a class
@@ -85,6 +91,30 @@ extension SdkHttpRequest {
8591
}
8692
}
8793

94+
public extension URLRequest {
95+
init(sdkRequest: SdkHttpRequest) async throws {
96+
// Set URL
97+
guard let url = sdkRequest.endpoint.url else {
98+
throw ClientError.dataNotFound("Failed to construct URLRequest due to missing URL.")
99+
}
100+
self.init(url: url)
101+
// Set method type
102+
self.httpMethod = sdkRequest.method.rawValue
103+
// Set body, handling any serialization errors
104+
do {
105+
self.httpBody = try await sdkRequest.body.readData()
106+
} catch {
107+
throw ClientError.serializationFailed("Failed to construct URLRequest due to HTTP body conversion failure.")
108+
}
109+
// Set headers
110+
sdkRequest.headers.headers.forEach { header in
111+
header.value.forEach { value in
112+
self.addValue(value, forHTTPHeaderField: header.name)
113+
}
114+
}
115+
}
116+
}
117+
88118
extension SdkHttpRequest: CustomDebugStringConvertible, CustomStringConvertible {
89119

90120
public var debugDescriptionWithBody: String {

Tests/ClientRuntimeTests/NetworkingTests/HttpRequestTests.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import XCTest
77
import AwsCommonRuntimeKit
88
import struct Foundation.URLQueryItem
99
@testable import ClientRuntime
10+
// In Linux, Foundation.URLRequest is moved to FoundationNetworking.
11+
#if canImport(FoundationNetworking)
12+
import FoundationNetworking
13+
#else
14+
import struct Foundation.URLRequest
15+
#endif
1016

1117
class HttpRequestTests: NetworkingTestUtils {
1218

@@ -51,6 +57,30 @@ class HttpRequestTests: NetworkingTestUtils {
5157
}
5258
}
5359

60+
func testSdkHttpRequestToURLRequest() async throws {
61+
let headers = Headers(["Testname-1": "testvalue-1", "Testname-2": "testvalue-2"])
62+
let endpoint = Endpoint(host: "host.com", path: "/", headers: headers)
63+
64+
let httpBody = HttpBody.data(expectedMockRequestData)
65+
let mockHttpRequest = SdkHttpRequest(method: .get, endpoint: endpoint, body: httpBody)
66+
let urlRequest = try await URLRequest(sdkRequest: mockHttpRequest)
67+
68+
XCTAssertNotNil(urlRequest)
69+
guard let headersFromRequest = urlRequest.allHTTPHeaderFields else {
70+
XCTFail("Headers in SdkHttpRequest were not successfully converted to headers in URLRequest.")
71+
// Compiler doesn't recognize XCTFail as return / exception thrown
72+
return
73+
}
74+
75+
// Check URLRequest fields
76+
XCTAssertTrue(headersFromRequest.contains { $0.key == "Testname-1" && $0.value == "testvalue-1" })
77+
XCTAssertTrue(headersFromRequest.contains { $0.key == "Testname-2" && $0.value == "testvalue-2" })
78+
let expectedBody = try await httpBody.readData()
79+
XCTAssertEqual(urlRequest.httpBody, expectedBody)
80+
XCTAssertEqual(urlRequest.url, endpoint.url)
81+
XCTAssertEqual(urlRequest.httpMethod, mockHttpRequest.method.rawValue)
82+
}
83+
5484
func testCRTHeadersToSdkHeaders() throws {
5585
let builder = SdkHttpRequestBuilder()
5686
.withHeader(name: "Host", value: "amazon.aws.com")

smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ImportDeclarations.kt

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ package software.amazon.smithy.swift.codegen
88
class ImportDeclarations {
99
private val imports = mutableSetOf<ImportStatement>()
1010

11-
fun addImport(packageName: String, isTestable: Boolean = false, internalSPIName: String? = null) {
11+
fun addImport(
12+
packageName: String,
13+
isTestable: Boolean = false,
14+
internalSPIName: String? = null,
15+
importOnlyIfCanImport: Boolean = false
16+
) {
1217
val existingImport = imports.find { it.packageName == packageName }
1318
if (existingImport != null) {
1419
// Update isTestable to true if needed
@@ -17,10 +22,12 @@ class ImportDeclarations {
1722
if (internalSPIName != null) {
1823
existingImport.internalSPINames.add(internalSPIName)
1924
}
25+
// Update importOnlyIfCanImport to true if needed
26+
existingImport.importOnlyIfCanImport = existingImport.importOnlyIfCanImport || importOnlyIfCanImport
2027
} else {
2128
// Otherwise, we have a new package to import, so add it
2229
val internalSPINames = listOf(internalSPIName).mapNotNull { it }.toMutableSet()
23-
imports.add(ImportStatement(packageName, isTestable, internalSPINames))
30+
imports.add(ImportStatement(packageName, isTestable, internalSPINames, importOnlyIfCanImport))
2431
}
2532
}
2633

@@ -36,7 +43,12 @@ class ImportDeclarations {
3643
}
3744
}
3845

39-
private data class ImportStatement(val packageName: String, var isTestable: Boolean, val internalSPINames: MutableSet<String>) {
46+
private data class ImportStatement(
47+
val packageName: String,
48+
var isTestable: Boolean,
49+
val internalSPINames: MutableSet<String>,
50+
var importOnlyIfCanImport: Boolean
51+
) {
4052
val statement: String
4153
get() {
4254
var import = "import $packageName"
@@ -46,6 +58,14 @@ private data class ImportStatement(val packageName: String, var isTestable: Bool
4658
if (isTestable) {
4759
import = "@testable $import"
4860
}
61+
if (importOnlyIfCanImport) {
62+
import =
63+
"""
64+
#if canImport($packageName)
65+
$import
66+
#endif
67+
""".trimIndent()
68+
}
4969
return import
5070
}
5171

smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftWriter.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,13 @@ class SwiftWriter(private val fullPackageName: String) : CodeWriter() {
101101
}
102102
}
103103

104-
fun addImport(packageName: String, isTestable: Boolean = false, internalSPIName: String? = null) {
105-
imports.addImport(packageName, isTestable, internalSPIName)
104+
fun addImport(
105+
packageName: String,
106+
isTestable: Boolean = false,
107+
internalSPIName: String? = null,
108+
importOnlyIfCanImport: Boolean = false
109+
) {
110+
imports.addImport(packageName, isTestable, internalSPIName, importOnlyIfCanImport)
106111
}
107112

108113
fun addFoundationImport() {

0 commit comments

Comments
 (0)