Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
495f2e1
Disable input/output serializers
jbelkins Nov 4, 2025
ca5b331
add ShapeID, doc comments
jbelkins Nov 5, 2025
212120e
Re-enable serde code
jbelkins Nov 5, 2025
fcdf1bf
Cleanup
jbelkins Nov 5, 2025
b2152c2
Merge branch 'main' into jbe/add_schema
jbelkins Nov 7, 2025
88f7be9
Merge branch 'main' into jbe/add_schema
jbelkins Nov 8, 2025
aa823be
feat: Add Swift-native codegen plugin
jbelkins Nov 9, 2025
e817d55
Fix lint
jbelkins Nov 9, 2025
6a9c537
Make service trait optional
jbelkins Nov 9, 2025
33cc093
Fix ktlint again
jbelkins Nov 9, 2025
6b54575
Refactor code generator plugin
jbelkins Nov 9, 2025
ae474f1
Convert smithy model info file to .json
jbelkins Nov 9, 2025
70f7669
Fix Swift 5.9 URL initializer
jbelkins Nov 9, 2025
3d623e6
Code cleanup
jbelkins Nov 9, 2025
d535e66
Revert disabling of schemas
jbelkins Nov 9, 2025
52f9532
Schemas generate for entire SDK
jbelkins Nov 15, 2025
1fa7fbd
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Nov 19, 2025
70e8a15
Fix method name renderSchema
jbelkins Nov 19, 2025
b1548a7
Fix swiftlint
jbelkins Nov 19, 2025
94a901d
Fix ktlint
jbelkins Nov 19, 2025
54a4300
Fix SmithyCodegenCoreTests
jbelkins Nov 19, 2025
6b860df
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Nov 22, 2025
1eb996b
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Nov 26, 2025
3fd9854
Cleanup
jbelkins Dec 5, 2025
64dd13a
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Dec 9, 2025
6a65655
Fixes
jbelkins Dec 9, 2025
db5b76a
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Dec 12, 2025
64eeff9
More codegen added
jbelkins Dec 12, 2025
953e1d9
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Dec 15, 2025
260e5ca
Serializes to string
jbelkins Dec 17, 2025
2e73154
Serializes maps & lists to string
jbelkins Dec 17, 2025
0a3b8c6
Fix rendered string for structure
jbelkins Dec 18, 2025
05d8088
Convert ShapeSerializer methods to throwing
jbelkins Dec 19, 2025
dbfb2af
Add deser code
jbelkins Dec 22, 2025
b19ab8c
Clarify doc comment
jbelkins Dec 22, 2025
d99a7c0
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Dec 22, 2025
811c8cc
Comments, fix swiftlint
jbelkins Dec 22, 2025
1005d47
Refine serializer interfaces
jbelkins Dec 22, 2025
707ab1a
refactor, added codec
jbelkins Dec 26, 2025
46b0808
Separate methods named sdkId, prune model to one service
jbelkins Dec 31, 2025
bf7b1b3
Protocol tests now run
jbelkins Jan 1, 2026
6550aab
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Jan 1, 2026
5ecb766
CBOR serialization passes protocol tests
jbelkins Jan 5, 2026
7a2eb11
All CBOR protocol tests pass, with local CRT changes
jbelkins Jan 7, 2026
82196f9
Fix swiftlint, ktlint, refactor
jbelkins Jan 8, 2026
6a07b2d
Added ClientProtocol
jbelkins Jan 12, 2026
aab0ecc
immediateDescendant refactor
jbelkins Jan 12, 2026
ee07905
Add query compatibility extensions
jbelkins Jan 13, 2026
faba2fc
Enable query mode
jbelkins Jan 14, 2026
9929a5f
Convert schemas to struct
jbelkins Jan 14, 2026
1257136
Add query compatibility, fix tests
jbelkins Jan 15, 2026
9ac9104
Fix Swift unit tests
jbelkins Jan 15, 2026
0abd9bc
No model info file for non-schema based svcs
jbelkins Jan 15, 2026
30c3184
TypeRegistry sendable
jbelkins Jan 15, 2026
36348a3
DeserializableShape: Sendable
jbelkins Jan 15, 2026
8ff310f
Refactor query compatible from TypeRegistry to ClientProtocol impl
jbelkins Jan 15, 2026
5908312
Fix swiftlint, rename
jbelkins Jan 15, 2026
1ba6543
Move query compatibility logic out
jbelkins Jan 15, 2026
8cb08ab
Fixes & model transforms so all but 6 svcs build
jbelkins Jan 17, 2026
87e8112
Whole SDK builds schema-based code now
jbelkins Jan 17, 2026
fe5aa9f
Fix compile warnings
jbelkins Jan 19, 2026
49cf3c6
Code cleanup
jbelkins Jan 19, 2026
7ce66d4
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Jan 19, 2026
4f961f7
Fix lint
jbelkins Jan 19, 2026
797ddd1
Simplify deser interfaces, test StringSerializer
jbelkins Jan 21, 2026
8ee1e87
Add clarifying comments on force unwraps
jbelkins Jan 22, 2026
4b2d01f
Merge branch 'jbe/codegen_core' into jbe/swift_codegen_schema
jbelkins Jan 24, 2026
67a016b
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Feb 2, 2026
3e47875
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Feb 3, 2026
3b423c5
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Feb 4, 2026
367ab7d
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Feb 9, 2026
b2b2351
CBOR uses plugin for path
jbelkins Feb 10, 2026
15d5e8d
Restore deprecations, fix lint
jbelkins Feb 10, 2026
1ab0a7f
Fix build for all services
jbelkins Feb 11, 2026
8256563
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Feb 12, 2026
29c0786
Internal clients generate compiling code
jbelkins Feb 13, 2026
f4f8a81
Fix lint
jbelkins Feb 13, 2026
4971dfc
Fix broken tests
jbelkins Feb 13, 2026
9e8d3cd
AWS JSON svcs build with schema-based
jbelkins Feb 13, 2026
f90b41d
Fix SmithyModelInfo whitespace
jbelkins Feb 13, 2026
61bed21
Fix lint
jbelkins Feb 16, 2026
ff47f1f
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Feb 16, 2026
bcd6dc7
Fix trailing comma in Package.swift
jbelkins Feb 16, 2026
85b016d
AWSJSON protocol tests now pass
jbelkins Feb 20, 2026
1686cdb
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Feb 20, 2026
09818e2
Pin swift-argument-parser while awaiting resolution of Swift 5.x issues
jbelkins Feb 20, 2026
ef21da0
Merge branch 'epic/sbs' into jbe/swift_codegen_schema
jbelkins Feb 20, 2026
1126577
Code cleanup, fix tests
jbelkins Feb 20, 2026
cfbca36
Fix lint
jbelkins Feb 20, 2026
4d43f12
Fix Swift 6 compile issue
jbelkins Feb 20, 2026
eec5714
More Swift 6 fixes
jbelkins Feb 20, 2026
ae04109
More Swift 6 fixes
jbelkins Feb 20, 2026
b2ee536
Even more Swift 6 fixes
jbelkins Feb 21, 2026
ac75b38
Get client name from settings sdkId
jbelkins Feb 23, 2026
242814d
Fix event stream protocol test build
jbelkins Feb 23, 2026
24a1bb9
Disable initial response integration
jbelkins Feb 23, 2026
d4109c1
Fix ktlint
jbelkins Feb 23, 2026
9fe9d83
Only add operations CLI param if operations is non empty
jbelkins Feb 24, 2026
b3c3a03
Remove timeout on smithy-swift CI
jbelkins Feb 24, 2026
a4f284a
feat: Add operation and type registry codegen
jbelkins Feb 26, 2026
52a642c
Fix SmithyCodegenCoreTests
jbelkins Feb 26, 2026
3eb8e82
Add support for service closure renames
jbelkins Feb 26, 2026
b1b4b55
Merge branch 'jbe/operations_and_type_registry' into jbe/swift_codege…
jbelkins Feb 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ jobs:
- name: Build & Run smithy-swift Kotlin Unit Tests
run: ./gradlew build
- name: Build & Run smithy-swift Swift Unit Tests
timeout-minutes: 15
run: |
set -o pipefail && \
NSUnbufferedIO=YES xcodebuild \
Expand Down
16 changes: 14 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ let package = Package(
],
products: [
.library(name: "Smithy", targets: ["Smithy"]),
.library(name: "RPCv2CBOR", targets: ["RPCv2CBOR"]),
.library(name: "SmithySerialization", targets: ["SmithySerialization"]),
.library(name: "ClientRuntime", targets: ["ClientRuntime"]),
.library(name: "SmithyRetriesAPI", targets: ["SmithyRetriesAPI"]),
Expand Down Expand Up @@ -286,8 +287,6 @@ let package = Package(
.target(
name: "SmithyCBOR",
dependencies: [
"SmithyReadWrite",
"SmithyTimestamps",
"Smithy",
"SmithySerialization",
.product(name: "AwsCommonRuntimeKit", package: "aws-crt-swift")
Expand Down Expand Up @@ -318,6 +317,15 @@ let package = Package(
],
resources: [ .process("Resources") ]
),
.target(
name: "RPCv2CBOR",
dependencies: [
"ClientRuntime",
"Smithy",
"SmithySerialization",
"SmithyCBOR",
]
),
.testTarget(
name: "ClientRuntimeTests",
dependencies: [
Expand Down Expand Up @@ -410,5 +418,9 @@ let package = Package(
dependencies: ["SmithyCodegenCore"],
resources: [ .process("Resources") ]
),
.testTarget(
name: "SmithySerializationTests",
dependencies: ["SmithySerialization"]
),
].compactMap { $0 }
)
64 changes: 50 additions & 14 deletions Plugins/SmithyCodeGeneratorPlugin/SmithyCodeGeneratorPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,22 @@ struct SmithyCodeGeneratorPlugin: BuildToolPlugin {
in outputDirectoryPath: Path,
with generatorToolPath: Path
) throws -> Command? {
// Skip any file that isn't the smithy-model-info.json for this service.
guard inputPath.lastComponent == "smithy-model-info.json" else { return nil }
// Skip any file that isn't the swift-settings.json for this service.
guard inputPath.lastComponent == "swift-settings.json" else { return nil }

let currentWorkingDirectoryFileURL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)

// Get the smithy-model-info.json file contents.
let modelInfoData = try Data(contentsOf: URL(fileURLWithPath: inputPath.string))
let smithyModelInfo = try JSONDecoder().decode(SmithyModelInfo.self, from: modelInfoData)

// Get the service ID & model path & settings sdkId from smithy-model-info.
// Get the fields from smithy-model-info.
let service = smithyModelInfo.service
let modelPathURL = currentWorkingDirectoryFileURL.appendingPathComponent(smithyModelInfo.path)
let sdkId = smithyModelInfo.sdkId
let modelPathURL = currentWorkingDirectoryFileURL.appendingPathComponent(smithyModelInfo.modelPath)
let modelPath = Path(modelPathURL.path)
let internalClient = smithyModelInfo.internalClient
let operations = smithyModelInfo.operations.joined(separator: ",")

// Construct the Schemas.swift path.
let schemasSwiftPath = outputDirectoryPath.appending("\(name)Schemas.swift")
Expand All @@ -61,32 +64,65 @@ struct SmithyCodeGeneratorPlugin: BuildToolPlugin {
// Construct the Deserialize.swift path.
let deserializeSwiftPath = outputDirectoryPath.appending("\(name)Deserialize.swift")

// Construct the Deserialize.swift path.
let typeRegistrySwiftPath = outputDirectoryPath.appending("\(name)TypeRegistry.swift")

// Construct the Operations.swift path.
let operationsSwiftPath = outputDirectoryPath.appending("\(name)Operations.swift")

var arguments: [any CustomStringConvertible] = [
service,
modelPath,
"--internal", "\(internalClient)",
"--sdk-id", sdkId,
"--schemas-path", schemasSwiftPath,
"--serialize-path", serializeSwiftPath,
"--deserialize-path", deserializeSwiftPath,
"--type-registry-path", typeRegistrySwiftPath,
"--operations-path", operationsSwiftPath,
"--schemas-path", schemasSwiftPath,
"--serialize-path", serializeSwiftPath,
"--deserialize-path", deserializeSwiftPath
]

if !operations.isEmpty {
arguments.append(contentsOf: ["--operations", operations])
}

// Construct the build command that invokes SmithyCodegenCLI.
return .buildCommand(
displayName: "Generating Swift source files from model file \(smithyModelInfo.path)",
displayName: "Generating Swift source files from model file \(smithyModelInfo.modelPath)",
executable: generatorToolPath,
arguments: [
service,
modelPath,
"--schemas-path", schemasSwiftPath,
"--serialize-path", serializeSwiftPath,
"--deserialize-path", deserializeSwiftPath
],
arguments: arguments,
inputFiles: [inputPath, modelPath],
outputFiles: [
schemasSwiftPath,
serializeSwiftPath,
deserializeSwiftPath,
typeRegistrySwiftPath,
operationsSwiftPath,
]
)
}
}

/// Codable structure for reading the contents of `smithy-model-info.json`
/// Decodable structure for reading the contents of `smithy-model-info.json`
private struct SmithyModelInfo: Decodable {
/// The shape ID of the service being generated. Must exist in the model.
let service: String

/// The name to be used for the enclosing module.
let module: String

/// The `sdkId` used by the Smithy-based code generator.
let sdkId: String

/// Set to `true` if the client should be rendered for internal use.
let internalClient: Bool

/// A list of operations to be included in the client. If omitted or empty, all operations are included.
let operations: [String]

/// The path to the model, from the root of the target's project. Required.
let path: String
let modelPath: String
}
40 changes: 40 additions & 0 deletions Sources/ClientRuntime/Config/Context+SchemaBased.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import struct Smithy.AttributeKey
import class Smithy.Context
import class Smithy.ContextBuilder
import protocol SmithySerialization.ClientProtocol
import struct SmithySerialization.Operation
import protocol SmithySerialization.OperationProperties

extension Context {

func operation<Input, Output>() -> Operation<Input, Output>? {
get(key: operationContextKey) as? Operation<Input, Output>
}

var clientProtocol: (any ClientProtocol)? {
`get`(key: clientProtocolKey)
}
}

extension ContextBuilder {

func withOperation<Input, Output>(_ operation: SmithySerialization.Operation<Input, Output>) -> ContextBuilder {
attributes.set(key: operationContextKey, value: operation)
return self
}

func withClientProtocol(_ clientProtocol: any ClientProtocol) -> ContextBuilder {
attributes.set(key: clientProtocolKey, value: clientProtocol)
return self
}
}

private let operationContextKey = AttributeKey<any OperationProperties>(name: "OperationContextKey")
private let clientProtocolKey = AttributeKey<any ClientProtocol>(name: "ClientProtocolKey")
2 changes: 1 addition & 1 deletion Sources/ClientRuntime/Networking/Http/HTTPError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ import class SmithyHTTPAPI.HTTPResponse
public protocol HTTPError {

/// The HTTP/HTTPS response that resulted in this error.
var httpResponse: HTTPResponse { get }
var httpResponse: HTTPResponse { get set }
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ extension DeserializeMiddleware: ResponseMessageDeserializer {
}

// Calculates & returns estimated skew.
func getEstimatedSkew(now: Date, responseDateString: String) -> TimeInterval {
public func getEstimatedSkew(now: Date, responseDateString: String) -> TimeInterval {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss z"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import struct Foundation.Data
import Smithy
import SmithyHTTPAPI
import protocol SmithySerialization.ClientProtocol
import protocol SmithySerialization.Codec
import protocol SmithySerialization.DeserializableStruct
import struct SmithySerialization.Operation
import protocol SmithySerialization.SerializableStruct

public struct SchemaBodyMiddleware<Input: SerializableStruct, Output: DeserializableStruct, CP: ClientProtocol> {
public let id: Swift.String = "BodyMiddleware"
let operation: Operation<Input, Output>
let clientProtocol: CP

public init(_ operation: Operation<Input, Output>, _ clientProtocol: CP) {
self.operation = operation
self.clientProtocol = clientProtocol
}
}

extension SchemaBodyMiddleware: RequestMessageSerializer {
public typealias InputType = Input
public typealias RequestType = CP.RequestType

public func apply(input: Input, builder: RequestType.RequestBuilderType, attributes: Context) throws {
do {
return try clientProtocol.serializeRequest(
operation: operation,
input: input,
requestBuilder: builder,
context: attributes
)
} catch {
throw ClientError.serializationFailed(error.localizedDescription)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import struct Foundation.Data
import struct Foundation.Date
import class Foundation.DateFormatter
import struct Foundation.Locale
import struct Foundation.TimeInterval
import struct Foundation.TimeZone
import struct Foundation.UUID
import class Smithy.Context
import protocol Smithy.ResponseMessageDeserializer
import SmithyHTTPAPI
import protocol SmithySerialization.ClientProtocol
import protocol SmithySerialization.DeserializableStruct
import struct SmithySerialization.Operation
import protocol SmithySerialization.SerializableStruct

public struct SchemaDeserializeMiddleware<
Input: SerializableStruct,
Output: DeserializableStruct,
ClientProtocol: SmithySerialization.ClientProtocol
> where ClientProtocol.ResponseType == HTTPResponse {
public var id: String = "Deserialize"
let operation: Operation<Input, Output>
let clientProtocol: ClientProtocol

public init(_ operation: Operation<Input, Output>, _ clientProtocol: ClientProtocol) {
self.operation = operation
self.clientProtocol = clientProtocol
}
}

extension SchemaDeserializeMiddleware: ResponseMessageDeserializer {

public func deserialize(response: ClientProtocol.ResponseType, attributes: Context) async throws -> Output {
if let responseDateString = response.headers.value(for: "Date") {
let estimatedSkew = getEstimatedSkew(now: Date(), responseDateString: responseDateString)
attributes.estimatedSkew = estimatedSkew
}

// check if the response body was affected by a previous middleware
if let contextBody = attributes.httpResponse?.body {
response.body = contextBody
}

// if the response is an error and the response body is a stream, we need to cache the stream so
// that it can be read again.
//
// error deserialization reads the stream multiple times to first deserialize the protocol error
// eg. [RestJSONError](https://github.com/awslabs/aws-sdk-swift/blob/d1d18eefb7457ed27d416b372573a1f815004eb1/Sources/Core/AWSClientRuntime/Protocols/RestJSON/RestJSONError.swift#L38,
// and then the service error eg. [AccountNotFoundException](https://github.com/awslabs/aws-sdk-swift/blob/d1d18eefb7457ed27d416b372573a1f815004eb1/Sources/Services/AWSCloudTrail/models/Models.swift#L62)
if response.statusCode.rawValue > 299 {
let bodyData = try await response.body.readData()
response.body = .data(bodyData)
}

return try await clientProtocol.deserializeResponse(
operation: operation,
context: attributes,
response: response
)
}
}
2 changes: 1 addition & 1 deletion Sources/ClientRuntime/Networking/ServiceError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public protocol ServiceError {
var typeName: String? { get }

/// The message for this error, if one was received.
var message: String? { get }
var message: String? { get set }
}

extension ServiceError {
Expand Down
15 changes: 15 additions & 0 deletions Sources/ClientRuntime/Orchestrator/OrchestratorBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ import protocol Smithy.RequestMessage
import protocol Smithy.RequestMessageSerializer
import protocol Smithy.ResponseMessage
import protocol Smithy.ResponseMessageDeserializer
import class SmithyHTTPAPI.HTTPResponse
import struct SmithyHTTPAuthAPI.SelectedAuthScheme
import struct SmithyRetriesAPI.RetryErrorInfo
import protocol SmithyRetriesAPI.RetryStrategy
import protocol SmithySerialization.ClientProtocol
import struct SmithySerialization.Operation

/// Builds an Orchestrator, combining runtime components, interceptors, serializers, and deserializers.
///
Expand Down Expand Up @@ -52,6 +55,18 @@ public class OrchestratorBuilder<
return self
}

@discardableResult
public func apply<ClientProtocol: SmithySerialization.ClientProtocol>(
_ operation: Operation<InputType, OutputType>,
_ clientProtocol: ClientProtocol
) -> Self where ClientProtocol.RequestType == RequestType,
ClientProtocol.ResponseType == ResponseType,
ResponseType == HTTPResponse {
self.serialize(SchemaBodyMiddleware(operation, clientProtocol))
self.deserialize(SchemaDeserializeMiddleware(operation, clientProtocol))
return self
}

/// - Parameter serializer: Function that performs part of request serialization
/// - Returns: Builder
@discardableResult
Expand Down
Loading
Loading