Skip to content

Commit 1337b73

Browse files
committed
rework schemas
1 parent e14223a commit 1337b73

File tree

58 files changed

+2368
-1431
lines changed

Some content is hidden

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

58 files changed

+2368
-1431
lines changed

packages/core/src/submodules/cbor/CborCodec.ts

+48-42
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
1-
import { deref, ListSchema, MapSchema, StructureSchema } from "@smithy/core/schema";
1+
import { NormalizedSchema } from "@smithy/core/schema";
22
import { copyDocumentWithTransform, parseEpochTimestamp } from "@smithy/core/serde";
3-
import {
4-
Codec,
5-
MemberSchema,
6-
Schema,
7-
SchemaRef,
8-
SerdeContext,
9-
ShapeDeserializer,
10-
ShapeSerializer,
11-
TraitsSchema,
12-
} from "@smithy/types";
3+
import { Codec, Schema, SchemaRef, SerdeContext, ShapeDeserializer, ShapeSerializer } from "@smithy/types";
134

145
import { cbor } from "./cbor";
156
import { dateToTag } from "./parseCborBody";
@@ -22,6 +13,7 @@ export class CborCodec implements Codec<Uint8Array, Uint8Array> {
2213
serializer.setSerdeContext(this.serdeContext!);
2314
return serializer;
2415
}
16+
2517
public createDeserializer(): CborShapeDeserializer {
2618
const deserializer = new CborShapeDeserializer();
2719
deserializer.setSerdeContext(this.serdeContext!);
@@ -46,17 +38,19 @@ export class CborShapeSerializer implements ShapeSerializer<Uint8Array> {
4638
if (_ instanceof Date) {
4739
return dateToTag(_);
4840
}
49-
const schema = deref((schemaRef as MemberSchema)?.[0] ?? schemaRef);
5041
if (_ instanceof Uint8Array) {
5142
return _;
5243
}
53-
const sparse = (schema as TraitsSchema)?.traits?.sparse;
44+
45+
const ns = NormalizedSchema.of(schemaRef);
46+
const sparse = !!ns.getMergedTraits().sparse;
47+
5448
if (Array.isArray(_)) {
5549
if (!sparse) {
5650
return _.filter((item) => item != null);
5751
}
5852
} else if (_ && typeof _ === "object") {
59-
if (!sparse) {
53+
if (!sparse || ns.isStructSchema()) {
6054
for (const [k, v] of Object.entries(_)) {
6155
if (v == null) {
6256
delete _[k];
@@ -65,6 +59,7 @@ export class CborShapeSerializer implements ShapeSerializer<Uint8Array> {
6559
return _;
6660
}
6761
}
62+
6863
return _;
6964
});
7065
}
@@ -88,15 +83,20 @@ export class CborShapeDeserializer implements ShapeDeserializer {
8883
return this.readValue(schema, data);
8984
}
9085

91-
private readValue(schema: Schema, value: any): any {
92-
if (typeof schema === "string") {
93-
if (schema === "time" || schema === "epoch-seconds" || schema === "date-time") {
86+
private readValue(_schema: Schema, value: any): any {
87+
const ns = NormalizedSchema.of(_schema);
88+
const schema = ns.getSchema();
89+
90+
if (typeof schema === "number") {
91+
if (ns.isTimestampSchema()) {
92+
// format is ignored.
9493
return parseEpochTimestamp(value);
9594
}
96-
if (schema === "blob" || schema === "streaming-blob") {
95+
if (ns.isBlobSchema()) {
9796
return value;
9897
}
9998
}
99+
100100
switch (typeof value) {
101101
case "undefined":
102102
case "boolean":
@@ -116,38 +116,44 @@ export class CborShapeDeserializer implements ShapeDeserializer {
116116
if (value instanceof Date) {
117117
return value;
118118
}
119-
const traits =
120-
Array.isArray(schema) && schema.length >= 2
121-
? {
122-
...(deref((schema as MemberSchema)[0]) as TraitsSchema)?.traits,
123-
...(schema as MemberSchema)[1],
124-
}
125-
: (deref(schema) as TraitsSchema)?.traits;
126-
127-
if (Array.isArray(value)) {
119+
if (ns.isDocumentSchema()) {
120+
return value;
121+
}
122+
123+
if (ns.isListSchema()) {
128124
const newArray = [];
125+
const memberSchema = ns.getValueSchema();
126+
const sparse = ns.isListSchema() && !!ns.getMergedTraits().sparse;
127+
129128
for (const item of value) {
130-
newArray.push(this.readValue(schema instanceof ListSchema ? deref(schema.valueSchema) : void 0, item));
131-
if (!traits?.sparse) {
132-
if (newArray[newArray.length - 1] == null) {
133-
newArray.pop();
134-
}
129+
newArray.push(this.readValue(memberSchema, item));
130+
if (!sparse && newArray[newArray.length - 1] == null) {
131+
newArray.pop();
135132
}
136133
}
137134
return newArray;
138135
}
139136

140137
const newObject = {} as any;
141-
for (const key of Object.keys(value)) {
142-
const targetSchema =
143-
schema instanceof StructureSchema
144-
? deref(schema.members[key]?.[0])
145-
: schema instanceof MapSchema
146-
? deref(schema.valueSchema)
147-
: void 0;
148-
newObject[key] = this.readValue(targetSchema, value[key]);
149-
if (!traits?.sparse && newObject[key] == null) {
150-
delete newObject[key];
138+
139+
if (ns.isMapSchema()) {
140+
const sparse = ns.getMergedTraits().sparse;
141+
const targetSchema = ns.getValueSchema();
142+
143+
for (const key of Object.keys(value)) {
144+
newObject[key] = this.readValue(targetSchema, value[key]);
145+
146+
if (newObject[key] == null && !sparse) {
147+
delete newObject[key];
148+
}
149+
}
150+
} else if (ns.isStructSchema()) {
151+
for (const key of Object.keys(value)) {
152+
const targetSchema = ns.getMemberSchema(key);
153+
if (targetSchema === undefined) {
154+
continue;
155+
}
156+
newObject[key] = this.readValue(targetSchema, value[key]);
151157
}
152158
}
153159
return newObject;

packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.spec.ts

+51-50
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { list, map, struct } from "@smithy/core/schema";
1+
import { list, map, SCHEMA, struct } from "@smithy/core/schema";
22
import { HttpRequest, HttpResponse } from "@smithy/protocol-http";
33
import { SchemaRef } from "@smithy/types";
4-
import { toBase64 } from "@smithy/util-base64";
54
import { describe, expect, test as it } from "vitest";
65

76
import { cbor } from "./cbor";
@@ -24,12 +23,14 @@ describe(SmithyRpcV2CborProtocol.name, () => {
2423
{
2524
name: "document with timestamp and blob",
2625
schema: struct(
26+
"",
2727
"MyExtendedDocument",
2828
{},
29-
{
30-
timestamp: [() => "time", {}],
31-
blob: [() => "blob", {}],
32-
}
29+
["timestamp", "blob"],
30+
[
31+
[SCHEMA.TIMESTAMP_DEFAULT, 0],
32+
[SCHEMA.BLOB, 0],
33+
]
3334
),
3435
input: {
3536
bool: true,
@@ -41,36 +42,25 @@ describe(SmithyRpcV2CborProtocol.name, () => {
4142
expected: {
4243
request: {},
4344
body: {
44-
bool: true,
45-
int: 5,
46-
float: -3.001,
4745
timestamp: dateToTag(new Date(1_000_000)),
4846
blob: bytes([97, 98, 99, 100]),
4947
},
5048
},
5149
},
5250
{
53-
name: "write to header and query",
51+
name: "do not write to header or query",
5452
schema: struct(
53+
"",
5554
"MyExtendedDocument",
5655
{},
57-
{
58-
bool: [, { httpQuery: "bool" }],
59-
timestamp: [
60-
() => "time",
61-
{
62-
httpHeader: "timestamp",
63-
},
64-
],
65-
blob: [
66-
() => "blob",
67-
{
68-
httpHeader: "blob",
69-
},
70-
],
71-
prefixHeaders: [, { httpPrefixHeaders: "anti-" }],
72-
searchParams: [, { httpQueryParams: {} }],
73-
}
56+
["bool", "timestamp", "blob", "prefixHeaders", "searchParams"],
57+
[
58+
[SCHEMA.BOOLEAN, { httpQuery: "bool" }],
59+
[SCHEMA.TIMESTAMP_DEFAULT, { httpHeader: "timestamp" }],
60+
[SCHEMA.BLOB, { httpHeader: "blob" }],
61+
[SCHEMA.MAP_MODIFIER | SCHEMA.STRING, { httpPrefixHeaders: "anti-" }],
62+
[SCHEMA.MAP_MODIFIER | SCHEMA.STRING, { httpQueryParams: 1 }],
63+
]
7464
),
7565
input: {
7666
bool: true,
@@ -87,28 +77,37 @@ describe(SmithyRpcV2CborProtocol.name, () => {
8777
},
8878
expected: {
8979
request: {
90-
headers: {
91-
timestamp: new Date(1_000_000).toISOString(),
92-
blob: toBase64(bytes([97, 98, 99, 100])),
93-
"anti-clockwise": "left",
94-
"anti-pasto": "cheese dodecahedron",
80+
headers: {},
81+
query: {},
82+
},
83+
body: {
84+
bool: true,
85+
timestamp: dateToTag(new Date(1_000_000)),
86+
blob: bytes([97, 98, 99, 100]),
87+
prefixHeaders: {
88+
pasto: "cheese dodecahedron",
89+
clockwise: "left",
90+
},
91+
searchParams: {
92+
a: 1,
93+
b: 2,
9594
},
96-
query: { bool: "true", a: "1", b: "2" },
9795
},
98-
body: {},
9996
},
10097
},
10198
{
10299
name: "sparse list and map",
103100
schema: struct(
101+
"",
104102
"MyShape",
105-
{},
106-
{
107-
mySparseList: [() => list("MyList", { sparse: 1 }), {}],
108-
myRegularList: [() => list("MyList", {}), {}],
109-
mySparseMap: [() => map("MyMap", { sparse: 1 }), {}],
110-
myRegularMap: [() => map("MyMap", {}), {}],
111-
}
103+
0,
104+
["mySparseList", "myRegularList", "mySparseMap", "myRegularMap"],
105+
[
106+
[() => list("", "MyList", { sparse: 1 }, SCHEMA.NUMERIC), {}],
107+
[() => list("", "MyList", {}, SCHEMA.NUMERIC), {}],
108+
[() => map("", "MyMap", { sparse: 1 }, SCHEMA.NUMERIC), {}],
109+
[() => map("", "MyMap", {}, SCHEMA.NUMERIC), {}],
110+
]
112111
),
113112
input: {
114113
mySparseList: [null, 1, null, 2, null],
@@ -154,10 +153,10 @@ describe(SmithyRpcV2CborProtocol.name, () => {
154153
const protocol = new SmithyRpcV2CborProtocol();
155154
const httpRequest = await protocol.serializeRequest(
156155
{
156+
name: "dummy",
157157
input: testCase.schema,
158158
output: void 0,
159159
traits: {},
160-
errors: [],
161160
},
162161
testCase.input,
163162
{
@@ -197,14 +196,16 @@ describe(SmithyRpcV2CborProtocol.name, () => {
197196
{
198197
name: "sparse list and map",
199198
schema: struct(
199+
"",
200200
"MyShape",
201-
{},
202-
{
203-
mySparseList: [() => list("MyList", { sparse: 1 }), {}],
204-
myRegularList: [() => list("MyList", {}), {}],
205-
mySparseMap: [() => map("MyMap", { sparse: 1 }), {}],
206-
myRegularMap: [() => map("MyMap", {}), {}],
207-
}
201+
0,
202+
["mySparseList", "myRegularList", "mySparseMap", "myRegularMap"],
203+
[
204+
[() => list("", "MyList", { sparse: 1 }, SCHEMA.NUMERIC), {}],
205+
[() => list("", "MyList", {}, SCHEMA.NUMERIC), {}],
206+
[() => map("", "MyMap", { sparse: 1 }, SCHEMA.NUMERIC), {}],
207+
[() => map("", "MyMap", {}, SCHEMA.NUMERIC), {}],
208+
]
208209
),
209210
mockOutput: {
210211
mySparseList: [null, 1, null, 2, null],
@@ -249,10 +250,10 @@ describe(SmithyRpcV2CborProtocol.name, () => {
249250
const protocol = new SmithyRpcV2CborProtocol();
250251
const output = await protocol.deserializeResponse(
251252
{
253+
name: "dummy",
252254
input: void 0,
253255
output: testCase.schema,
254256
traits: {},
255-
errors: [],
256257
},
257258
{},
258259
new HttpResponse({

packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HttpInterceptingShapeSerializer,HttpProtocol } from "@smithy/core/protocols";
1+
import { RpcProtocol } from "@smithy/core/protocols";
22
import { deref, ErrorSchema, OperationSchema, TypeRegistry } from "@smithy/core/schema";
33
import type {
44
HandlerExecutionContext,
@@ -9,18 +9,22 @@ import type {
99
} from "@smithy/types";
1010
import { getSmithyContext } from "@smithy/util-middleware";
1111

12-
import { CborCodec, CborShapeSerializer } from "./CborCodec";
12+
import { CborCodec } from "./CborCodec";
1313
import { loadSmithyRpcV2CborErrorCode } from "./parseCborBody";
1414

15-
export class SmithyRpcV2CborProtocol extends HttpProtocol {
15+
export class SmithyRpcV2CborProtocol extends RpcProtocol {
1616
private codec = new CborCodec();
17-
protected serializer = new HttpInterceptingShapeSerializer<CborShapeSerializer>(this.codec.createSerializer());
17+
protected serializer = this.codec.createSerializer();
1818
protected deserializer = this.codec.createDeserializer();
1919

2020
public getShapeId(): string {
2121
return "smithy.protocols#rpcv2Cbor";
2222
}
2323

24+
public getPayloadCodec(): CborCodec {
25+
return this.codec;
26+
}
27+
2428
public async serializeRequest<Input extends object>(
2529
operationSchema: OperationSchema,
2630
input: Input,
@@ -36,6 +40,10 @@ export class SmithyRpcV2CborProtocol extends HttpProtocol {
3640
delete request.body;
3741
delete request.headers["content-type"];
3842
} else {
43+
if (!request.body) {
44+
this.serializer.write(15, {});
45+
request.body = this.serializer.flush();
46+
}
3947
try {
4048
request.headers["content-length"] = String((request.body as Uint8Array).byteLength);
4149
} catch (e) {}
@@ -82,6 +90,7 @@ export class SmithyRpcV2CborProtocol extends HttpProtocol {
8290
// TODO(schema) throw client base exception using the dataObject.
8391
throw new Error("schema not found for " + error);
8492
}
93+
8594
const message = dataObject.message ?? dataObject.Message ?? "Unknown";
8695
const exception = new errorSchema.ctor(message);
8796
Object.assign(exception, {

0 commit comments

Comments
 (0)