Skip to content

Commit ef9d7b0

Browse files
Dillon Nysdnys1
authored andcommitted
chore(smithy,3.0): Simplify serialization
Makes serialization more statically typed. Reduces conflation of Input and InputPayload types. And makes the serialization pipeline more clear overall by tightening API definitions.
1 parent eb3524b commit ef9d7b0

17 files changed

+129
-152
lines changed

packages/smithy/goldens/tool/generate.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ include: package:lints/recommended.yaml
248248
analyzer:
249249
errors:
250250
avoid_unused_constructor_parameters: ignore
251+
deprecated_member_use_from_same_package: ignore
251252
non_constant_identifier_names: ignore
252-
prefer_interpolation_to_compose_strings: ignore
253253
''');
254254

255255
// Create mono_pkg for testing

packages/smithy/smithy/lib/src/http/http_operation.dart

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,7 @@ abstract class HttpOperation<InputPayload, Input, OutputPayload, Output>
6161
/// Builds the output from the [payload] and metadata from the HTTP
6262
/// [response].
6363
Output buildOutput(
64-
// This is (kind of) a hack to allow `OutputPayload` to always be non-null
65-
// even if the payload type is nullable. We need the non-null version to
66-
// interop with built_value correctly.
67-
covariant Object? payload,
64+
OutputPayload payload,
6865
AWSBaseHttpResponse response,
6966
);
7067

@@ -171,7 +168,7 @@ abstract class HttpOperation<InputPayload, Input, OutputPayload, Output>
171168
...request.queryParameters.asMap(),
172169
...uri.queryParametersAll,
173170
};
174-
final body = protocol.serialize(input, specifiedType: FullType(Input));
171+
final body = protocol.serialize(input);
175172

176173
final awsRequest = AWSStreamedHttpRequest.raw(
177174
method: AWSHttpMethod.fromString(request.method),
@@ -270,10 +267,7 @@ abstract class HttpOperation<InputPayload, Input, OutputPayload, Output>
270267
StackTrace? stackTrace;
271268
var successCode = this.successCode();
272269
try {
273-
final payload = await protocol.deserialize(
274-
response.split(),
275-
specifiedType: FullType(OutputPayload),
276-
);
270+
final payload = await protocol.deserialize(response.split());
277271
output = switch (payload) {
278272
Output _ => payload,
279273
_ => buildOutput(payload, response),
@@ -300,8 +294,9 @@ abstract class HttpOperation<InputPayload, Input, OutputPayload, Output>
300294
smithyError =
301295
errorTypes.firstWhereOrNull((t) => t.shapeId.shape == resolvedType);
302296
}
303-
smithyError ??= errorTypes
304-
.singleWhereOrNull((t) => t.statusCode == response.statusCode);
297+
smithyError ??= errorTypes.singleWhereOrNull(
298+
(t) => t.statusCode == response.statusCode,
299+
);
305300
if (smithyError == null) {
306301
throw SmithyHttpException(
307302
statusCode: response.statusCode,
@@ -311,8 +306,8 @@ abstract class HttpOperation<InputPayload, Input, OutputPayload, Output>
311306
}
312307
final errorType = smithyError.type;
313308
final builder = smithyError.builder;
314-
final errorPayload = await protocol.deserialize(
315-
response.body,
309+
final errorPayload = await protocol.wireSerializer.deserialize(
310+
await response.bodyBytes,
316311
specifiedType: FullType(errorType),
317312
);
318313
final smithyException =

packages/smithy/smithy/lib/src/http/http_protocol.dart

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import 'dart:convert';
5+
import 'dart:typed_data';
56

67
import 'package:async/async.dart';
78
import 'package:aws_common/aws_common.dart';
@@ -12,7 +13,7 @@ import 'package:smithy/smithy.dart';
1213
/// A protocol for sending requests over HTTP.
1314
@optionalTypeArgs
1415
abstract class HttpProtocol<InputPayload, Input, OutputPayload, Output>
15-
implements Protocol<Input, Output, Stream<List<int>>> {
16+
implements Protocol<Input, OutputPayload> {
1617
const HttpProtocol();
1718

1819
/// The content type of the request payload, added to the `Content-Type`
@@ -21,7 +22,7 @@ abstract class HttpProtocol<InputPayload, Input, OutputPayload, Output>
2122

2223
/// Protocol headers
2324
Map<String, String> get headers => {
24-
'Content-Type': contentType,
25+
AWSHeaders.contentType: contentType,
2526
};
2627

2728
Serializers get serializers;
@@ -45,45 +46,49 @@ abstract class HttpProtocol<InputPayload, Input, OutputPayload, Output>
4546
return AWSHttpClient()..supportedProtocols = SupportedProtocols.http1;
4647
}
4748

48-
@override
49-
Stream<List<int>> serialize(Object? input, {FullType? specifiedType}) {
50-
final Object? payload = input is HasPayload ? input.getPayload() : input;
49+
/// Serializes [input] to an HTTP body.
50+
Stream<List<int>> serialize(Input input) {
51+
final payload = switch (input) {
52+
InputPayload _ => input,
53+
_ as HasPayload<InputPayload> => input.getPayload(),
54+
};
5155
return switch (payload) {
56+
null => const Stream.empty(),
5257
String _ => Stream.value(utf8.encode(payload)),
5358
List<int> _ => Stream.value(payload),
5459
Stream<List<int>> _ => payload,
5560
_ => Stream.fromFuture(
5661
Future.value(
5762
wireSerializer.serialize(
58-
input,
59-
specifiedType:
60-
specifiedType ?? FullType(Input, [FullType(InputPayload)]),
63+
payload,
64+
// Even though we're serializing the payload, specify the
65+
// [Input] type since the semantics of serializing [Input]
66+
// vs [InputPayload] vary. For example, some traits
67+
// may only apply to the payload when serializing it as part
68+
// of an [Input] struct vs. when directly serialized.
69+
//
70+
// We further pass the [InputPayloas] so that our built_value plugins
71+
// have both types which is helpful when making determinations
72+
// about how to, for example, process a List<Object?> which
73+
// could represent either a Map or a List.
74+
specifiedType: FullType(Input, [FullType(InputPayload)]),
6175
),
6276
),
6377
),
6478
};
6579
}
6680

67-
@override
68-
Future<Object?> deserialize(
69-
Stream<List<int>> response, {
70-
FullType? specifiedType,
71-
}) async {
72-
specifiedType ??= FullType(OutputPayload);
73-
if (specifiedType.root == OutputPayload &&
74-
OutputPayload == Stream<List<int>>) {
75-
return response;
76-
}
77-
78-
final body = await collectBytes(response);
79-
if (specifiedType.root == OutputPayload) {
80-
if (OutputPayload == List<int>) {
81-
return body;
82-
} else if (OutputPayload == String) {
83-
return body.isEmpty ? '' : utf8.decode(body);
84-
}
85-
}
86-
return await wireSerializer.deserialize(body, specifiedType: specifiedType);
81+
/// Deserializes an HTTP [response] body to an [OutputPayload].
82+
Future<OutputPayload> deserialize(Stream<List<int>> response) async {
83+
return switch (OutputPayload) {
84+
const (Stream<List<int>>) => response,
85+
const (List<int>) || const (Uint8List) => await collectBytes(response),
86+
const (String) => await utf8.decodeStream(response),
87+
_ => await wireSerializer.deserialize(
88+
await collectBytes(response),
89+
specifiedType: FullType(OutputPayload),
90+
),
91+
} as OutputPayload;
8792
}
8893
}
8994

@@ -101,7 +106,7 @@ mixin HasLabel {
101106
String labelFor(String key);
102107
}
103108

104-
abstract interface class HasPayload<Payload> {
109+
abstract interface class HasPayload<Payload extends Object?> {
105110
Payload? getPayload();
106111
}
107112

packages/smithy/smithy/lib/src/http/http_server.dart

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import 'package:async/async.dart';
45
import 'package:aws_common/aws_common.dart';
56
import 'package:built_value/serializer.dart';
67
import 'package:shelf/shelf.dart';
@@ -30,11 +31,24 @@ abstract class HttpServerBase implements FullSerializer<Stream<List<int>>> {
3031

3132
@override
3233
Stream<List<int>> serialize(Object? input, {FullType? specifiedType}) =>
33-
protocol.serialize(input, specifiedType: specifiedType);
34+
Stream.fromFuture(
35+
Future.value(
36+
protocol.wireSerializer.serialize(
37+
input,
38+
specifiedType: specifiedType,
39+
),
40+
),
41+
);
3442

3543
@override
36-
Object? deserialize(Stream<List<int>> input, {FullType? specifiedType}) =>
37-
protocol.deserialize(input, specifiedType: specifiedType);
44+
Future<Object?> deserialize(
45+
Stream<List<int>> output, {
46+
FullType? specifiedType,
47+
}) async =>
48+
protocol.wireSerializer.deserialize(
49+
await collectBytes(output),
50+
specifiedType: specifiedType,
51+
);
3852

3953
Future<Response> handleUncaughtError(Object error, StackTrace st);
4054
}

packages/smithy/smithy/lib/src/protocol/protocol.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import 'package:smithy/smithy.dart';
88

99
/// Metadata related to an operation regarding its serialization format,
1010
/// authentication schemes, etc.
11-
abstract class Protocol<Input, Output, WireType>
12-
implements FullSerializer<WireType> {
11+
abstract class Protocol<Input, Output> {
1312
const Protocol._();
1413

1514
/// The shape ID of the protocol trait this class implements.
@@ -22,5 +21,5 @@ abstract class FullSerializer<WireType>
2221
FutureOr<WireType> serialize(Object? input, {FullType? specifiedType});
2322

2423
@override
25-
FutureOr<Object?> deserialize(WireType input, {FullType? specifiedType});
24+
FutureOr<Object?> deserialize(WireType output, {FullType? specifiedType});
2625
}

packages/smithy/smithy/lib/src/serialization/xml/xml_bool_serializer.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class XmlBoolSerializer extends PrimitiveSmithySerializer<bool> {
99
const XmlBoolSerializer() : super('Bool');
1010

1111
@override
12-
Iterable<Type> get types => [bool];
12+
Iterable<Type> get types => const [bool];
1313

1414
@override
1515
bool deserialize(

packages/smithy/smithy_aws/lib/src/protocol/event_stream_protocol.dart

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import 'package:aws_common/aws_common.dart';
55
import 'package:built_value/serializer.dart';
66
import 'package:smithy/smithy.dart';
7-
87
import 'package:smithy_aws/src/protocol/aws_http_protocol.dart';
98

109
class EventStreamProtocol<InputPayload, Input, OutputPayload, Output>
@@ -18,14 +17,11 @@ class EventStreamProtocol<InputPayload, Input, OutputPayload, Output>
1817
String get contentType => _baseProtocol.contentType;
1918

2019
@override
21-
Future<Object?> deserialize(
22-
Stream<List<int>> response, {
23-
FullType? specifiedType,
24-
}) async {
20+
Future<OutputPayload> deserialize(Stream<List<int>> response) async {
2521
return serializers.deserialize(
2622
response,
2723
specifiedType: FullType(OutputPayload),
28-
);
24+
) as OutputPayload;
2925
}
3026

3127
@override
@@ -50,8 +46,7 @@ class EventStreamProtocol<InputPayload, Input, OutputPayload, Output>
5046
_baseProtocol.responseInterceptors;
5147

5248
@override
53-
Stream<List<int>> serialize(Object? input, {FullType? specifiedType}) =>
54-
_baseProtocol.serialize(input, specifiedType: specifiedType);
49+
Stream<List<int>> serialize(Input input) => _baseProtocol.serialize(input);
5550

5651
@override
5752
Serializers get serializers => _baseProtocol.serializers;

packages/smithy/smithy_aws/lib/src/protocol/rest_xml.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ class RestXmlProtocol<InputPayload, Input, OutputPayload, Output>
6565
'application/xml';
6666

6767
@override
68-
late final FullSerializer<List<int>> wireSerializer =
69-
XmlSerializer(serializers);
68+
late final XmlSerializer wireSerializer = XmlSerializer(serializers);
7069

7170
static Future<String?> resolveError(AWSBaseHttpResponse response) async {
7271
try {

packages/smithy/smithy_codegen/lib/src/core/reserved_words.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ const structReservedWords = [
115115
'serializer',
116116
'replace',
117117
'toBuilder',
118+
'object',
118119
];
119120

120121
const reservedTypeNames = [

packages/smithy/smithy_codegen/lib/src/generate.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,17 @@ import 'package:smithy_codegen/smithy_codegen.dart';
77
import 'package:smithy_codegen/src/generator/visitors/library_visitor.dart';
88
import 'package:smithy_codegen/src/version.dart';
99

10+
const List<String> _ignoredRules = [
11+
'avoid_unused_constructor_parameters',
12+
'deprecated_member_use_from_same_package',
13+
'non_constant_identifier_names',
14+
];
15+
1016
/// Header which prefixes all generated files.
11-
const String header =
12-
'// Generated with smithy-dart $packageVersion. DO NOT MODIFY.';
17+
final String header = '''
18+
// Generated with smithy-dart $packageVersion. DO NOT MODIFY.
19+
// ignore_for_file: ${_ignoredRules.join(',')}
20+
''';
1321

1422
/// The default emitter for codegen operations.
1523
DartEmitter buildEmitter(Allocator allocator) => DartEmitter(

0 commit comments

Comments
 (0)