Skip to content

Commit 93d04cc

Browse files
authored
feat: Schema-based AWS JSON 1.0 / 1.1, RPCv2CBOR (#1071)
1 parent bdc4295 commit 93d04cc

213 files changed

Lines changed: 12732 additions & 1161 deletions

File tree

Some content is hidden

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

.github/workflows/continuous-integration.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ jobs:
6868
run: xcrun simctl list
6969
- name: Build & Run smithy-swift Kotlin Unit Tests
7070
run: ./gradlew build
71+
- name: Generate test SDKs
72+
run: ./scripts/codegen.sh
7173
- name: Build & Run smithy-swift Swift Unit Tests
7274
run: |
7375
set -o pipefail && \
@@ -206,6 +208,8 @@ jobs:
206208
uses: ./.github/actions/setup-common-tools-composite-action
207209
- name: Build & Run Kotlin Unit Tests
208210
run: ./gradlew build
211+
- name: Generate test SDKs
212+
run: ./scripts/codegen.sh
209213
- name: Build & Run Swift Unit Tests
210214
run: swift test
211215

.github/workflows/lint.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ jobs:
3232
swiftlint --version
3333
- name: Run swiftlint
3434
run: swiftlint --strict --reporter github-actions-logging
35+
- name: Generate smithy-swift test SDK
36+
run: ./scripts/codegen.sh
3537
- name: Compile project with Xcode
3638
timeout-minutes: 20
3739
run: xcodebuild -scheme smithy-swift-Package -destination platform=macOS > xcodebuild.log

.github/workflows/swift6-compatibility.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ jobs:
2323
run: sudo xcode-select -s /Applications/Xcode_26.4.app
2424
- name: Setup common tools
2525
uses: ./.github/actions/setup-common-tools-composite-action
26+
- name: Generate test SDKs
27+
run: ./scripts/codegen.sh
2628
- name: Build on swift-tools-version 6.0
2729
run: |
2830
swift package tools-version --set 6.0

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ xcuserdata
3131

3232
# Codegen build products
3333
build/
34+
smithy-build.json
3435

3536
# VS Code config files
3637
.vscode

.swiftlint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ excluded:
22
- .build
33
- Sources/SmithyTestUtil/*
44
- Tests/*
5+
- test-sdks/*
56

67
analyzer_rules:
78
- unused_import

Package.swift

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ let package = Package(
3131
products: [
3232
.library(name: "Smithy", targets: ["Smithy"]),
3333
.library(name: "SmithySerialization", targets: ["SmithySerialization"]),
34+
.library(name: "SmithyAWSJSON", targets: ["SmithyAWSJSON"]),
35+
.library(name: "SmithyRPCv2CBOR", targets: ["SmithyRPCv2CBOR"]),
3436
.library(name: "ClientRuntime", targets: ["ClientRuntime"]),
3537
.library(name: "SmithyRetriesAPI", targets: ["SmithyRetriesAPI"]),
3638
.library(name: "SmithyRetries", targets: ["SmithyRetries"]),
@@ -51,16 +53,17 @@ let package = Package(
5153
.library(name: "SmithyStreams", targets: ["SmithyStreams"]),
5254
.library(name: "SmithyChecksumsAPI", targets: ["SmithyChecksumsAPI"]),
5355
.library(name: "SmithyChecksums", targets: ["SmithyChecksums"]),
54-
.library(name: "SmithyCBOR", targets: ["SmithyCBOR"]),
5556
.library(name: "SmithyWaitersAPI", targets: ["SmithyWaitersAPI"]),
5657
.library(name: "SmithyTestUtil", targets: ["SmithyTestUtil"]),
5758
.library(name: "SmithySwiftNIO", targets: ["SmithySwiftNIO"]),
5859
.library(name: "SmithyTelemetryAPI", targets: ["SmithyTelemetryAPI"]),
5960
.library(name: "SmithyHTTPClientAPI", targets: ["SmithyHTTPClientAPI"]),
61+
.plugin(name: "SmithyCodeGeneratorPlugin", targets: ["SmithyCodeGeneratorPlugin"]),
6062
],
6163
dependencies: {
6264
var dependencies: [Package.Dependency] = [
6365
.package(url: "https://github.com/awslabs/aws-crt-swift.git", exact: "0.58.1"),
66+
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.1.0"),
6467
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
6568
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.22.0"),
6669
]
@@ -118,6 +121,7 @@ let package = Package(
118121
"SmithyChecksumsAPI",
119122
"SmithyChecksums",
120123
"SmithyCBOR",
124+
"SmithySerialization",
121125
.product(name: "AwsCommonRuntimeKit", package: "aws-crt-swift"),
122126
],
123127
resources: [
@@ -231,6 +235,7 @@ let package = Package(
231235
name: "SmithyEventStreams",
232236
dependencies: [
233237
"Smithy",
238+
"SmithySerialization",
234239
"SmithyEventStreamsAPI",
235240
"SmithyEventStreamsAuthAPI",
236241
.product(name: "AwsCommonRuntimeKit", package: "aws-crt-swift")
@@ -261,14 +266,55 @@ let package = Package(
261266
.target(
262267
name: "SmithyCBOR",
263268
dependencies: [
264-
"SmithyReadWrite",
265-
"SmithyTimestamps",
269+
"Smithy",
270+
"SmithySerialization",
266271
.product(name: "AwsCommonRuntimeKit", package: "aws-crt-swift")
267272
]
268273
),
269274
.target(
270275
name: "SmithyWaitersAPI"
271276
),
277+
.plugin(
278+
name: "SmithyCodeGeneratorPlugin",
279+
capability: .buildTool(),
280+
dependencies: [
281+
"SmithyCodegenCLI",
282+
]
283+
),
284+
.executableTarget(
285+
name: "SmithyCodegenCLI",
286+
dependencies: [
287+
"SmithyCodegenCore",
288+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
289+
]
290+
),
291+
.target(
292+
name: "SmithyCodegenCore",
293+
dependencies: [
294+
"Smithy",
295+
"SmithySerialization",
296+
],
297+
resources: [ .process("Resources") ]
298+
),
299+
.target(
300+
name: "SmithyAWSJSON",
301+
dependencies: [
302+
"ClientRuntime",
303+
"Smithy",
304+
"SmithySerialization",
305+
"SmithyJSON",
306+
"SmithyEventStreams",
307+
]
308+
),
309+
.target(
310+
name: "SmithyRPCv2CBOR",
311+
dependencies: [
312+
"ClientRuntime",
313+
"Smithy",
314+
"SmithySerialization",
315+
"SmithyCBOR",
316+
]
317+
),
272318
.testTarget(
273319
name: "ClientRuntimeTests",
274320
dependencies: [
@@ -279,17 +325,17 @@ let package = Package(
279325
],
280326
resources: [ .process("Resources") ]
281327
),
328+
.testTarget(
329+
name: "SmithyTests",
330+
dependencies: ["Smithy"]
331+
),
282332
.testTarget(
283333
name: "SmithySwiftNIOTests",
284334
dependencies: [
285335
"SmithySwiftNIO",
286336
"SmithyTestUtil",
287337
]
288338
),
289-
.testTarget(
290-
name: "SmithyCBORTests",
291-
dependencies: ["SmithyCBOR", "ClientRuntime", "SmithyTestUtil"]
292-
),
293339
.testTarget(
294340
name: "SmithyHTTPClientTests",
295341
dependencies: [
@@ -352,5 +398,28 @@ let package = Package(
352398
name: "SmithyStreamsTests",
353399
dependencies: ["SmithyStreams", "Smithy"]
354400
),
401+
.testTarget(
402+
name: "SmithyCodegenCoreTests",
403+
dependencies: ["SmithyCodegenCore"],
404+
resources: [ .process("Resources") ]
405+
),
406+
.target(
407+
name: "RPCv2CBORTestSDK",
408+
dependencies: [
409+
"ClientRuntime",
410+
"SmithyHTTPAPI",
411+
"SmithyHTTPAuthAPI",
412+
"SmithyIdentity",
413+
"SmithyRPCv2CBOR",
414+
"SmithyRetries",
415+
"SmithyRetriesAPI",
416+
],
417+
path: "test-sdks/build/smithyprojections/test-sdks/rpcv2cbor/swift-codegen/Sources/RPCv2CBORTestSDK",
418+
plugins: ["SmithyCodeGeneratorPlugin"]
419+
),
420+
.testTarget(
421+
name: "SmithySerializationTests",
422+
dependencies: ["SmithySerialization", "RPCv2CBORTestSDK"]
423+
),
355424
].compactMap { $0 }
356425
)
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
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 struct Foundation.Data
9+
import class Foundation.FileManager
10+
import class Foundation.JSONDecoder
11+
import struct Foundation.URL
12+
import PackagePlugin
13+
14+
@main
15+
struct SmithyCodeGeneratorPlugin: BuildToolPlugin {
16+
17+
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
18+
// This plugin only runs for package targets that can have source files.
19+
guard let sourceFiles = target.sourceModule?.sourceFiles else { return [] }
20+
21+
// Retrieve the `SmithyCodegenCLI` tool from the plugin's tools.
22+
let smithyCodegenCLITool = try context.tool(named: "SmithyCodegenCLI")
23+
24+
// Construct a build command for each source file with a particular suffix.
25+
return try sourceFiles.map(\.path).compactMap {
26+
try createBuildCommand(
27+
name: target.name,
28+
for: $0,
29+
in: context.pluginWorkDirectory,
30+
with: smithyCodegenCLITool.path
31+
)
32+
}
33+
}
34+
35+
private func createBuildCommand(
36+
name: String,
37+
for inputPath: Path,
38+
in outputDirectoryPath: Path,
39+
with generatorToolPath: Path
40+
) throws -> Command? {
41+
// Skip any file that isn't the swift-settings.json for this service.
42+
guard inputPath.lastComponent == "swift-settings.json" else { return nil }
43+
44+
let currentWorkingDirectoryFileURL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
45+
46+
// Get the smithy-model-info.json file contents.
47+
let modelInfoData = try Data(contentsOf: URL(fileURLWithPath: inputPath.string))
48+
let smithyModelInfo = try JSONDecoder().decode(SmithyModelInfo.self, from: modelInfoData)
49+
50+
// Get the fields from smithy-model-info.
51+
let service = smithyModelInfo.service
52+
let sdkId = smithyModelInfo.sdkId
53+
let modelPathURL = if let modelPath = smithyModelInfo.modelPath {
54+
currentWorkingDirectoryFileURL.appendingPathComponent(modelPath)
55+
} else {
56+
URL(fileURLWithPath: inputPath.removingLastComponent().appending(["model.json"]).string)
57+
}
58+
let modelPath = Path(modelPathURL.path)
59+
let internalClient = smithyModelInfo.internalClient
60+
let operations = smithyModelInfo.operations.joined(separator: ",")
61+
62+
// Construct the Schemas.swift path.
63+
let schemasSwiftPath = outputDirectoryPath.appending("\(name)Schemas.swift")
64+
65+
// Construct the Serialize.swift path.
66+
let serializeSwiftPath = outputDirectoryPath.appending("\(name)Serialize.swift")
67+
68+
// Construct the Deserialize.swift path.
69+
let deserializeSwiftPath = outputDirectoryPath.appending("\(name)Deserialize.swift")
70+
71+
// Construct the TypeRegistry.swift path.
72+
let typeRegistrySwiftPath = outputDirectoryPath.appending("\(name)TypeRegistry.swift")
73+
74+
// Construct the Operations.swift path.
75+
let operationsSwiftPath = outputDirectoryPath.appending("\(name)Operations.swift")
76+
77+
var arguments: [any CustomStringConvertible] = [
78+
service,
79+
modelPath,
80+
"--internal", "\(internalClient)",
81+
"--sdk-id", sdkId,
82+
"--schemas-path", schemasSwiftPath,
83+
"--serialize-path", serializeSwiftPath,
84+
"--deserialize-path", deserializeSwiftPath,
85+
"--type-registry-path", typeRegistrySwiftPath,
86+
"--operations-path", operationsSwiftPath,
87+
]
88+
89+
if !operations.isEmpty {
90+
arguments.append(contentsOf: ["--operations", operations])
91+
}
92+
93+
// Construct the build command that invokes SmithyCodegenCLI.
94+
return .buildCommand(
95+
displayName: "Generating Swift source files from model file \(modelPath)",
96+
executable: generatorToolPath,
97+
arguments: arguments,
98+
inputFiles: [inputPath, modelPath],
99+
outputFiles: [
100+
schemasSwiftPath,
101+
serializeSwiftPath,
102+
deserializeSwiftPath,
103+
typeRegistrySwiftPath,
104+
operationsSwiftPath,
105+
]
106+
)
107+
}
108+
}
109+
110+
/// Decodable structure for reading the contents of `smithy-model-info.json`
111+
private struct SmithyModelInfo: Decodable {
112+
/// The shape ID of the service being generated. Must exist in the model.
113+
let service: String
114+
115+
/// The name to be used for the enclosing module.
116+
let module: String
117+
118+
/// The `sdkId` used by the Smithy-based code generator.
119+
let sdkId: String
120+
121+
/// Set to `true` if the client should be rendered for internal use.
122+
let internalClient: Bool
123+
124+
/// A list of operations to be included in the client. If omitted or empty, all operations are included.
125+
let operations: [String]
126+
127+
/// The path to the model, from the root of the target's project.
128+
let modelPath: String?
129+
}

Sources/ClientRuntime/Interceptor/DefaultInterceptorContext.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ extension DefaultInterceptorContext: BeforeDeserialization {
7070
}
7171

7272
extension DefaultInterceptorContext: MutableResponse {
73+
7374
public func updateResponse(updated: ResponseType) {
7475
self.response = updated
7576
}
@@ -93,9 +94,14 @@ extension DefaultInterceptorContext: AfterAttempt {
9394
}
9495

9596
extension DefaultInterceptorContext: MutableOutputAfterAttempt {
97+
9698
public func updateOutput(updated: OutputType) {
9799
self.result = .success(updated)
98100
}
101+
102+
public func updateError(updated: any Error) {
103+
self.result = .failure(updated)
104+
}
99105
}
100106

101107
extension DefaultInterceptorContext: Finalization {

Sources/ClientRuntime/Interceptor/InterceptorContext.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ public protocol MutableOutputAfterAttempt<InputType, OutputType, RequestType, Re
141141
/// Mutates the operation output.
142142
/// - Parameter updated: The updated output.
143143
func updateOutput(updated: OutputType)
144+
145+
/// Mutates the operation error.
146+
/// - Parameter updated: The updated error.
147+
func updateError(updated: any Error)
144148
}
145149

146150
/// Context given to interceptor hooks called after execution.
@@ -179,4 +183,8 @@ public protocol MutableOutputFinalization<InputType, OutputType, RequestType, Re
179183
/// Mutates the operation output.
180184
/// - Parameter updated: The updated output.
181185
func updateOutput(updated: OutputType)
186+
187+
/// Mutates the operation error.
188+
/// - Parameter updated: The updated error.
189+
func updateError(updated: any Error)
182190
}

Sources/ClientRuntime/Networking/Http/HTTPError.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ import class SmithyHTTPAPI.HTTPResponse
1212
public protocol HTTPError {
1313

1414
/// The HTTP/HTTPS response that resulted in this error.
15-
var httpResponse: HTTPResponse { get }
15+
var httpResponse: HTTPResponse { get set }
1616
}

0 commit comments

Comments
 (0)