Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Replace the XML encoder with a custom Smithy implementation #619

Merged
merged 42 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
fe2f813
Add an in-house XMLEncoder
jbelkins Nov 8, 2023
7e57bec
Encode with XML coding keys; set attribute as location when needed
jbelkins Nov 9, 2023
8407093
Fixes several protocol tests
jbelkins Nov 9, 2023
856fa83
WIP
jbelkins Nov 13, 2023
b6fce2f
Remove unneeded source files
jbelkins Nov 13, 2023
6cd8f5c
Simplify XML root node code
jbelkins Nov 13, 2023
0e04b4a
Lint fixes & cleanup
jbelkins Nov 13, 2023
b87aa0c
Cleanup & renaming
jbelkins Nov 14, 2023
b8ea90a
A little more cleanup
jbelkins Nov 14, 2023
fe89d94
Passes protocol tests but one, needs event streams
jbelkins Nov 16, 2023
11706e7
All protocol tests pass
jbelkins Nov 16, 2023
a694b95
Fix ktlint
jbelkins Nov 16, 2023
e7826a9
Fix Swiftlint
jbelkins Nov 16, 2023
75e4845
Fix codegen tests
jbelkins Nov 16, 2023
201ae22
Test fixes
jbelkins Nov 16, 2023
3769863
Re-enable event streams in middleware
jbelkins Nov 16, 2023
fe6f688
Fix many codegen tests
jbelkins Nov 16, 2023
14765de
Fixed even more codegen tests
jbelkins Nov 16, 2023
429827d
Even even more codegen tests fixed
jbelkins Nov 16, 2023
73e1a8f
Final codegen tests pass
jbelkins Nov 16, 2023
424afee
Fix swiftlint
jbelkins Nov 16, 2023
eccf8a5
Fix SmithyXML unit tests
jbelkins Nov 16, 2023
7261003
Fix SmithyTestUtils
jbelkins Nov 16, 2023
ab696cb
Add FoundationXML import for platforms that need it
jbelkins Nov 17, 2023
d91d2d9
More FoundationXML imports
jbelkins Nov 17, 2023
844529d
Refactor, Linux fixes
jbelkins Nov 17, 2023
fef742b
Refactor to use defer
jbelkins Nov 17, 2023
cf9b18e
Use libxml for XML parsing
jbelkins Nov 18, 2023
13fd680
Code cleanup
jbelkins Nov 18, 2023
421e7eb
Links libxml2 on AL2
jbelkins Nov 18, 2023
ab28846
Fix tests on Mac
jbelkins Nov 18, 2023
21ae6a1
Removed XMLDocument from float encoder tests
jbelkins Nov 18, 2023
42bec5a
Remove ReadWrite protocols
jbelkins Nov 19, 2023
0217884
Fix swiftlint
jbelkins Nov 19, 2023
98f9634
Rename linux libxml to match Mac
jbelkins Nov 21, 2023
edc803d
Fix modulemap
jbelkins Nov 21, 2023
9c67c59
Fix warning, provide writer method that takes writing closure
jbelkins Nov 26, 2023
29d139c
Fix tests
jbelkins Nov 26, 2023
c311b73
Merge remote-tracking branch 'origin/main' into jbe/xml
jbelkins Nov 26, 2023
e93ced4
Merge branch 'main' into jbe/xml
jbelkins Nov 28, 2023
3d4d97e
Code review feedback
jbelkins Nov 28, 2023
e2d5e9f
More Kotlin fixes
jbelkins Nov 28, 2023
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
2 changes: 2 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ excluded:
- Sources/SmithyTestUtil/*
- Tests/ClientRuntimeTests/*
- Tests/SmithyTestUtilTests/*
- Tests/SmithyXMLTests/*
- Tests/SmithyTimestampsTests/*

analyzer_rules:
- unused_import
Expand Down
41 changes: 36 additions & 5 deletions Package.swift
jbelkins marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ let package = Package(
],
products: [
.library(name: "ClientRuntime", targets: ["ClientRuntime"]),
.library(name: "SmithyTestUtil", targets: ["SmithyTestUtil"])
.library(name: "SmithyReadWrite", targets: ["SmithyReadWrite"]),
.library(name: "SmithyXML", targets: ["SmithyXML"]),
.library(name: "SmithyTestUtil", targets: ["SmithyTestUtil"]),
],
dependencies: [
.package(url: "https://github.com/awslabs/aws-crt-swift.git", exact: "0.17.0"),
Expand All @@ -21,22 +23,51 @@ let package = Package(
.target(
name: "ClientRuntime",
dependencies: [
"SmithyXML",
.product(name: "AwsCommonRuntimeKit", package: "aws-crt-swift"),
.product(name: "Logging", package: "swift-log"),
.product(name: "XMLCoder", package: "XMLCoder")
]
),
.testTarget(
name: "ClientRuntimeTests",
dependencies: ["ClientRuntime", "SmithyTestUtil"]
.target(name: "SmithyReadWrite"),
.target(
name: "SmithyXML",
dependencies: [
"SmithyReadWrite",
"SmithyTimestamps",
.target(name: "libxml2", condition: .when(platforms: [.linux]))
]
),
.systemLibrary(
name: "libxml2",
pkgConfig: "libxml-2.0",
providers: [
.apt(["libxml2 libxml2-dev"]),
.yum(["libxml2 libxml2-devel"])
]
),
.target(
name: "SmithyTimestamps"
),
.target(
name: "SmithyTestUtil",
dependencies: ["ClientRuntime"]
),
.testTarget(
name: "ClientRuntimeTests",
dependencies: ["ClientRuntime", "SmithyTestUtil"]
),
.testTarget(
name: "SmithyXMLTests",
dependencies: ["SmithyXML"]
),
.testTarget(
name: "SmithyTimestampsTests",
dependencies: ["SmithyTimestamps"]
),
.testTarget(
name: "SmithyTestUtilTests",
dependencies: ["SmithyTestUtil"]
)
),
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,27 @@ extension EventStream {
public class DefaultMessageEncoderStream<Event: MessageMarshallable>: MessageEncoderStream, Stream {
let stream: AsyncThrowingStream<Event, Error>
let messageEncoder: MessageEncoder
let messageSinger: MessageSigner
let messageSigner: MessageSigner
let requestEncoder: RequestEncoder
var readAsyncIterator: AsyncIterator?

public init(
stream: AsyncThrowingStream<Event, Error>,
messageEncoder: MessageEncoder,
requestEncoder: RequestEncoder,
messageSinger: MessageSigner
messageSigner: MessageSigner
) {
self.stream = stream
self.messageEncoder = messageEncoder
self.messageSinger = messageSinger
self.messageSigner = messageSigner
self.requestEncoder = requestEncoder
self.readAsyncIterator = makeAsyncIterator()
}

public struct AsyncIterator: AsyncIteratorProtocol {
let stream: AsyncThrowingStream<Event, Error>
let messageEncoder: MessageEncoder
var messageSinger: MessageSigner
var messageSigner: MessageSigner
let requestEncoder: RequestEncoder

private var lastMessageSent: Bool = false
Expand All @@ -42,12 +42,12 @@ extension EventStream {
stream: AsyncThrowingStream<Event, Error>,
messageEncoder: MessageEncoder,
requestEncoder: RequestEncoder,
messageSinger: MessageSigner
messageSigner: MessageSigner
) {
self.stream = stream
self.streamIterator = stream.makeAsyncIterator()
self.messageEncoder = messageEncoder
self.messageSinger = messageSinger
self.messageSigner = messageSigner
self.requestEncoder = requestEncoder
}

Expand All @@ -56,7 +56,7 @@ extension EventStream {
// There are no more messages in the base stream
// if we have not sent the last message, send it now
guard lastMessageSent else {
let emptySignedMessage = try await messageSinger.signEmpty()
let emptySignedMessage = try await messageSigner.signEmpty()
let data = try messageEncoder.encode(message: emptySignedMessage)
lastMessageSent = true
return data
Expand All @@ -70,7 +70,7 @@ extension EventStream {
let message = try event.marshall(encoder: requestEncoder)

// sign the message
let signedMessage = try await messageSinger.sign(message: message)
let signedMessage = try await messageSigner.sign(message: message)

// encode again the signed message
let data = try messageEncoder.encode(message: signedMessage)
Expand All @@ -83,7 +83,7 @@ extension EventStream {
stream: stream,
messageEncoder: messageEncoder,
requestEncoder: requestEncoder,
messageSinger: messageSinger
messageSigner: messageSigner
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import struct Foundation.Data

public struct BlobBodyMiddleware<OperationStackInput,
OperationStackOutput: HttpResponseBinding>: Middleware {
public let id: Swift.String = "BlobBodyMiddleware"

let keyPath: KeyPath<OperationStackInput, Data?>

public init(keyPath: KeyPath<OperationStackInput, Data?>) {
self.keyPath = keyPath
}

public func handle<H>(context: Context,
input: SerializeStepInput<OperationStackInput>,
next: H) async throws -> OperationOutput<OperationStackOutput>
where H: Handler,
Self.MInput == H.Input,
Self.MOutput == H.Output,
Self.Context == H.Context {
let body = HttpBody.data(input.operationInput[keyPath: keyPath])
input.builder.withBody(body)
return try await next.handle(context: context, input: input)
}

public typealias MInput = SerializeStepInput<OperationStackInput>
public typealias MOutput = OperationOutput<OperationStackOutput>
public typealias Context = HttpContext
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import struct Foundation.Data

public struct BlobStreamBodyMiddleware<OperationStackInput,
OperationStackOutput: HttpResponseBinding>: Middleware {
public let id: Swift.String = "BlobStreamBodyMiddleware"

let keyPath: KeyPath<OperationStackInput, ByteStream?>

public init(keyPath: KeyPath<OperationStackInput, ByteStream?>) {
self.keyPath = keyPath
}

public func handle<H>(context: Context,
input: SerializeStepInput<OperationStackInput>,
next: H) async throws -> OperationOutput<OperationStackOutput>
where H: Handler,
Self.MInput == H.Input,
Self.MOutput == H.Output,
Self.Context == H.Context {
if let byteStream = input.operationInput[keyPath: keyPath] {
let body = HttpBody(byteStream: byteStream)
input.builder.withBody(body)
}
return try await next.handle(context: context, input: input)
}

public typealias MInput = SerializeStepInput<OperationStackInput>
public typealias MOutput = OperationOutput<OperationStackOutput>
public typealias Context = HttpContext
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@
// SPDX-License-Identifier: Apache-2.0
//

public struct SerializableBodyMiddleware<OperationStackInput: Encodable,
OperationStackOutput: HttpResponseBinding>: Middleware {
public let id: Swift.String = "\(String(describing: OperationStackInput.self))BodyMiddleware"
import struct Foundation.Data
import typealias SmithyReadWrite.DocumentWritingClosure
import typealias SmithyReadWrite.WritingClosure

let xmlName: String?
public struct BodyMiddleware<OperationStackInput,
OperationStackOutput: HttpResponseBinding,
Writer>: Middleware {
public let id: Swift.String = "BodyMiddleware"

public init(xmlName: String? = nil) {
self.xmlName = xmlName
let documentWritingClosure: DocumentWritingClosure<OperationStackInput, Writer>
let inputWritingClosure: WritingClosure<OperationStackInput, Writer>

public init(
documentWritingClosure: @escaping DocumentWritingClosure<OperationStackInput, Writer>,
inputWritingClosure: @escaping WritingClosure<OperationStackInput, Writer>
) {
self.documentWritingClosure = documentWritingClosure
self.inputWritingClosure = inputWritingClosure
}

public func handle<H>(context: Context,
Expand All @@ -23,13 +33,7 @@ public struct SerializableBodyMiddleware<OperationStackInput: Encodable,
Self.MOutput == H.Output,
Self.Context == H.Context {
do {
let encoder = context.getEncoder()
let data: Data
if let xmlName = xmlName, let xmlEncoder = encoder as? XMLEncoder {
data = try xmlEncoder.encode(input.operationInput, withRootKey: xmlName)
} else {
data = try encoder.encode(input.operationInput)
}
let data = try documentWritingClosure(input.operationInput, inputWritingClosure)
let body = HttpBody.data(data)
input.builder.withBody(body)
} catch {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import struct Foundation.Data

public struct EnumBodyMiddleware<OperationStackInput,
OperationStackOutput: HttpResponseBinding,
Primitive: RawRepresentable>: Middleware where Primitive.RawValue == String {
public let id: Swift.String = "EnumBodyMiddleware"

let keyPath: KeyPath<OperationStackInput, Primitive?>

public init(keyPath: KeyPath<OperationStackInput, Primitive?>) {
self.keyPath = keyPath
}

public func handle<H>(context: Context,
input: SerializeStepInput<OperationStackInput>,
next: H) async throws -> OperationOutput<OperationStackOutput>
where H: Handler,
Self.MInput == H.Input,
Self.MOutput == H.Output,
Self.Context == H.Context {
let bodyString = input.operationInput[keyPath: keyPath]?.rawValue ?? ""
let body = HttpBody.data(Data(bodyString.utf8))
input.builder.withBody(body)
return try await next.handle(context: context, input: input)
}

public typealias MInput = SerializeStepInput<OperationStackInput>
public typealias MOutput = OperationOutput<OperationStackOutput>
public typealias Context = HttpContext
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import struct Foundation.Data
import typealias SmithyReadWrite.DocumentWritingClosure
import typealias SmithyReadWrite.WritingClosure

public struct EventStreamBodyMiddleware<OperationStackInput,
OperationStackOutput: HttpResponseBinding,
OperationStackInputPayload: MessageMarshallable>:
Middleware {
public let id: Swift.String = "EventStreamBodyMiddleware"

let keyPath: KeyPath<OperationStackInput, AsyncThrowingStream<OperationStackInputPayload, Swift.Error>?>
let defaultBody: String?

public init(
keyPath: KeyPath<OperationStackInput, AsyncThrowingStream<OperationStackInputPayload, Swift.Error>?>,
defaultBody: String? = nil
) {
self.keyPath = keyPath
self.defaultBody = defaultBody
}

public func handle<H>(context: Context,
input: SerializeStepInput<OperationStackInput>,
next: H) async throws -> OperationOutput<OperationStackOutput>
where H: Handler,
Self.MInput == H.Input,
Self.MOutput == H.Output,
Self.Context == H.Context {
let encoder = context.getEncoder()
if let eventStream = input.operationInput[keyPath: keyPath] {
guard let messageEncoder = context.getMessageEncoder() else {
fatalError("Message encoder is required for streaming payload")
}
guard let messageSigner = context.getMessageSigner() else {
fatalError("Message signer is required for streaming payload")
}
let encoderStream = EventStream.DefaultMessageEncoderStream(
stream: eventStream,
messageEncoder: messageEncoder,
requestEncoder: encoder,
messageSigner: messageSigner
)
input.builder.withBody(.stream(encoderStream))
} else if let defaultBody {
input.builder.withBody(HttpBody.data(Data(defaultBody.utf8)))
}
return try await next.handle(context: context, input: input)
}

public typealias MInput = SerializeStepInput<OperationStackInput>
public typealias MOutput = OperationOutput<OperationStackOutput>
public typealias Context = HttpContext
}
Loading
Loading