-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into day/aws-chunked-encoding
- Loading branch information
Showing
17 changed files
with
478 additions
and
19 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
IntegrationTests/Services/AWSS3IntegrationTests/S3EventStreamTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// | ||
// Copyright Amazon.com Inc. or its affiliates. | ||
// All Rights Reserved. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
import Foundation | ||
import AWSS3 | ||
import XCTest | ||
|
||
class S3EventStreamTests: S3XCTestCase { | ||
private let objectKey = "integ-test-json-object" | ||
|
||
override func setUp() async throws { | ||
// Create S3 client & unique bucket with UUID. | ||
try await super.setUp() | ||
// Put a JSON object to the bucket. | ||
try await putObject( | ||
body: """ | ||
{ "Rules": [ {"id": "1"}, {"expr": "y > x"}, {"id": "2", "expr": "z = DEBUG"} ]} | ||
{ "created": "June 27", "modified": "July 6" } | ||
""", | ||
key: objectKey | ||
) | ||
} | ||
|
||
// Tests event stream output in restXml protocol using S3::SelectObjectContent. | ||
func testEventStreamOutput() async throws { | ||
let result = try await client.selectObjectContent(input: SelectObjectContentInput( | ||
bucket: bucketName, | ||
// Gets the two ID objects from the S3 object content. | ||
expression: "SELECT id FROM S3Object[*].Rules[*].id WHERE id IS NOT MISSING", | ||
expressionType: .sql, | ||
inputSerialization: S3ClientTypes.InputSerialization( | ||
json: S3ClientTypes.JSONInput(type: .document) | ||
), | ||
key: objectKey, | ||
outputSerialization: S3ClientTypes.OutputSerialization(json: S3ClientTypes.JSONOutput()) | ||
)) | ||
|
||
let outputStream = result.payload | ||
var actualOutput = "" | ||
|
||
for try await event in outputStream! { | ||
switch event { | ||
case .records(let record): | ||
actualOutput = actualOutput + (String(data: record.payload ?? Data(), encoding: .utf8) ?? "") | ||
case .stats, .end: | ||
continue | ||
case .sdkUnknown(let data): | ||
XCTFail(data) | ||
default: | ||
XCTFail("Encountered an unexpected event in output stream.") | ||
} | ||
} | ||
|
||
// Check returned record event's payload was successfully received. | ||
let expectedOutput = "{\"id\":\"1\"}\n{\"id\":\"2\"}\n" | ||
XCTAssertEqual(expectedOutput, actualOutput) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...t/codegen/MessageMarshallableGenerator.kt → ...n/message/MessageMarshallableGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 3 additions & 1 deletion
4
...codegen/MessageUnmarshallableGenerator.kt → ...message/MessageUnmarshallableGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
...otlin/software/amazon/smithy/aws/swift/codegen/message/XMLMessageMarshallableGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
package software.amazon.smithy.aws.swift.codegen.message | ||
|
||
import software.amazon.smithy.codegen.core.CodegenException | ||
import software.amazon.smithy.codegen.core.Symbol | ||
import software.amazon.smithy.model.shapes.MemberShape | ||
import software.amazon.smithy.model.shapes.Shape | ||
import software.amazon.smithy.model.shapes.ShapeType | ||
import software.amazon.smithy.model.shapes.UnionShape | ||
import software.amazon.smithy.model.traits.EventHeaderTrait | ||
import software.amazon.smithy.model.traits.EventPayloadTrait | ||
import software.amazon.smithy.swift.codegen.ClientRuntimeTypes | ||
import software.amazon.smithy.swift.codegen.SwiftDependency | ||
import software.amazon.smithy.swift.codegen.SwiftWriter | ||
import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator | ||
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.DocumentWritingClosureUtils | ||
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WritingClosureUtils | ||
import software.amazon.smithy.swift.codegen.model.eventStreamEvents | ||
import software.amazon.smithy.swift.codegen.model.hasTrait | ||
|
||
class XMLMessageMarshallableGenerator( | ||
private val ctx: ProtocolGenerator.GenerationContext, | ||
private val payloadContentType: String | ||
) { | ||
internal fun render(streamShape: UnionShape) { | ||
val streamSymbol: Symbol = ctx.symbolProvider.toSymbol(streamShape) | ||
val rootNamespace = ctx.settings.moduleName | ||
val streamMember = Symbol.builder() | ||
.definitionFile("./$rootNamespace/models/${streamSymbol.name}+MessageMarshallable.swift") | ||
.name(streamSymbol.name) | ||
.build() | ||
ctx.delegator.useShapeWriter(streamMember) { writer -> | ||
writer.apply { | ||
addImport(SwiftDependency.CLIENT_RUNTIME.target) | ||
openBlock("extension \$L {", "}", streamSymbol.fullName) { | ||
openBlock( | ||
"static var marshal: \$N<\$N> {", "}", | ||
ClientRuntimeTypes.EventStream.MarshalClosure, | ||
streamSymbol | ||
) { | ||
openBlock("{ (self) in", "}") { | ||
write( | ||
"var headers: [\$N] = [.init(name: \":message-type\", value: .string(\"event\"))]", | ||
ClientRuntimeTypes.EventStream.Header | ||
) | ||
write("var payload: \$D", ClientRuntimeTypes.Core.Data) | ||
write("switch self {") | ||
streamShape.eventStreamEvents(ctx.model).forEach { member -> | ||
val memberName = ctx.symbolProvider.toMemberName(member) | ||
write("case \$L(let value):", memberName) | ||
indent() | ||
addStringHeader(":event-type", member.memberName) | ||
val variant = ctx.model.expectShape(member.target) | ||
val eventHeaderBindings = variant.members().filter { | ||
it.hasTrait<EventHeaderTrait>() | ||
} | ||
val eventPayloadBinding = variant.members().firstOrNull { | ||
it.hasTrait<EventPayloadTrait>() | ||
} | ||
val unbound = variant.members().filterNot { | ||
it.hasTrait<EventHeaderTrait>() || it.hasTrait<EventPayloadTrait>() | ||
} | ||
|
||
eventHeaderBindings.forEach { | ||
renderSerializeEventHeader(ctx, it, writer) | ||
} | ||
|
||
when { | ||
eventPayloadBinding != null -> renderSerializeEventPayload(ctx, eventPayloadBinding, writer) | ||
unbound.isNotEmpty() -> { | ||
writer.addStringHeader(":content-type", payloadContentType) | ||
renderPayloadSerialization(ctx, writer, variant) | ||
} | ||
} | ||
writer.dedent() | ||
} | ||
writer.write("case .sdkUnknown(_):") | ||
writer.indent() | ||
writer.write( | ||
"throw \$N(\"cannot serialize the unknown event type!\")", | ||
ClientRuntimeTypes.Core.UnknownClientError | ||
) | ||
writer.dedent() | ||
writer.write("}") | ||
writer.write( | ||
"return \$N(headers: headers, payload: payload ?? .init())", | ||
ClientRuntimeTypes.EventStream.Message | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private fun renderSerializeEventPayload(ctx: ProtocolGenerator.GenerationContext, member: MemberShape, writer: SwiftWriter) { | ||
val target = ctx.model.expectShape(member.target) | ||
val memberName = ctx.symbolProvider.toMemberName(member) | ||
when (target.type) { | ||
ShapeType.BLOB -> { | ||
writer.addStringHeader(":content-type", "application/octet-stream") | ||
writer.write("payload = value.\$L", memberName) | ||
} | ||
ShapeType.STRING -> { | ||
writer.addStringHeader(":content-type", "text/plain") | ||
writer.write("payload = value.\$L?.data(using: .utf8)", memberName) | ||
} | ||
ShapeType.STRUCTURE, ShapeType.UNION -> { | ||
writer.addStringHeader(":content-type", payloadContentType) | ||
renderPayloadSerialization(ctx, writer, target) | ||
} | ||
else -> throw CodegenException("unsupported shape type `${target.type}` for target: $target; expected blob, string, structure, or union for eventPayload member: $member") | ||
} | ||
} | ||
|
||
/** | ||
* | ||
* if let headerValue = value.blob { | ||
* headers.append(.init(name: "blob", value: .byteArray(headerValue))) | ||
* } | ||
* if let headerValue = value.boolean { | ||
* headers.append(.init(name: "boolean", value: .bool(headerValue))) | ||
* } | ||
* if let headerValue = value.byte { | ||
* headers.append(.init(name: "byte", value: .byte(headerValue))) | ||
* } | ||
* if let headerValue = value.int { | ||
* headers.append(.init(name: "int", value: .int32(Int32(headerValue)))) | ||
* } | ||
* if let headerValue = value.long { | ||
* headers.append(.init(name: "long", value: .int64(Int64(headerValue)))) | ||
* } | ||
* if let headerValue = value.short { | ||
* headers.append(.init(name: "short", value: .int16(headerValue))) | ||
* } | ||
* if let headerValue = value.string { | ||
* headers.append(.init(name: "string", value: .string(headerValue))) | ||
* } | ||
* if let headerValue = value.timestamp { | ||
* headers.append(.init(name: "timestamp", value: .timestamp(headerValue))) | ||
* } | ||
*/ | ||
private fun renderSerializeEventHeader(ctx: ProtocolGenerator.GenerationContext, member: MemberShape, writer: SwiftWriter) { | ||
val target = ctx.model.expectShape(member.target) | ||
val headerValue = when (target.type) { | ||
ShapeType.BOOLEAN -> "bool" | ||
ShapeType.BYTE -> "byte" | ||
ShapeType.SHORT -> "int16" | ||
ShapeType.INTEGER -> "int32" | ||
ShapeType.LONG -> "int64" | ||
ShapeType.BLOB -> "byteArray" | ||
ShapeType.STRING -> "string" | ||
ShapeType.TIMESTAMP -> "timestamp" | ||
else -> throw CodegenException("unsupported shape type `${target.type}` for eventHeader member `$member`; target: $target") | ||
} | ||
|
||
val memberName = ctx.symbolProvider.toMemberName(member) | ||
writer.openBlock("if let headerValue = value.\$L {", "}", memberName) { | ||
when (target.type) { | ||
ShapeType.INTEGER -> { | ||
writer.write("headers.append(.init(name: \"${member.memberName}\", value: .\$L(Int32(headerValue))))", headerValue) | ||
} | ||
ShapeType.LONG -> { | ||
writer.write("headers.append(.init(name: \"${member.memberName}\", value: .\$L(Int64(headerValue))))", headerValue) | ||
} | ||
else -> { | ||
writer.write("headers.append(.init(name: \"${member.memberName}\", value: .\$L(headerValue)))", headerValue) | ||
} | ||
} | ||
} | ||
} | ||
|
||
private fun SwiftWriter.addStringHeader(name: String, value: String) { | ||
write("headers.append(.init(name: \$S, value: .string(\$S)))", name, value) | ||
} | ||
|
||
private fun renderPayloadSerialization(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, shape: Shape) { | ||
// get a payload serializer for the given members of the variant | ||
val documentWritingClosure = DocumentWritingClosureUtils(ctx, writer).closure(shape) | ||
val valueWritingClosure = WritingClosureUtils(ctx, writer).writingClosure(shape) | ||
writer.write("payload = try \$L(value, \$L)", documentWritingClosure, valueWritingClosure) | ||
} | ||
} |
4 changes: 3 additions & 1 deletion
4
...egen/XMLMessageUnmarshallableGenerator.kt → ...sage/XMLMessageUnmarshallableGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.