Skip to content

Commit 23812a9

Browse files
authored
feat(core): add rpcv2 cbor runtime protocol (#1601)
* feat(core): add rpcv2 cbor runtime protocol * add structIterator generator
1 parent ae11e3a commit 23812a9

23 files changed

+1779
-4
lines changed

.changeset/pretty-cows-call.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/core": minor
3+
---
4+
5+
add cbor protocol (alpha)

.changeset/two-berries-repeat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/smithy-client": minor
3+
---
4+
5+
add schema property to Command class

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"@smithy/middleware-serde": "workspace:^",
7373
"@smithy/protocol-http": "workspace:^",
7474
"@smithy/types": "workspace:^",
75+
"@smithy/util-base64": "workspace:^",
7576
"@smithy/util-body-length-browser": "workspace:^",
7677
"@smithy/util-middleware": "workspace:^",
7778
"@smithy/util-stream": "workspace:^",
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { NormalizedSchema } from "@smithy/core/schema";
2+
import { copyDocumentWithTransform, parseEpochTimestamp } from "@smithy/core/serde";
3+
import { Codec, Schema, SchemaRef, SerdeFunctions, ShapeDeserializer, ShapeSerializer } from "@smithy/types";
4+
5+
import { cbor } from "./cbor";
6+
import { dateToTag } from "./parseCborBody";
7+
8+
/**
9+
* @alpha
10+
*/
11+
export class CborCodec implements Codec<Uint8Array, Uint8Array> {
12+
private serdeContext?: SerdeFunctions;
13+
14+
public createSerializer(): CborShapeSerializer {
15+
const serializer = new CborShapeSerializer();
16+
serializer.setSerdeContext(this.serdeContext!);
17+
return serializer;
18+
}
19+
20+
public createDeserializer(): CborShapeDeserializer {
21+
const deserializer = new CborShapeDeserializer();
22+
deserializer.setSerdeContext(this.serdeContext!);
23+
return deserializer;
24+
}
25+
26+
public setSerdeContext(serdeContext: SerdeFunctions): void {
27+
this.serdeContext = serdeContext;
28+
}
29+
}
30+
31+
/**
32+
* @alpha
33+
*/
34+
export class CborShapeSerializer implements ShapeSerializer {
35+
private serdeContext?: SerdeFunctions;
36+
private value: unknown;
37+
38+
public setSerdeContext(serdeContext: SerdeFunctions) {
39+
this.serdeContext = serdeContext;
40+
}
41+
42+
public write(schema: Schema, value: unknown): void {
43+
this.value = copyDocumentWithTransform(value, schema, (_: any, schemaRef: SchemaRef) => {
44+
if (_ instanceof Date) {
45+
return dateToTag(_);
46+
}
47+
if (_ instanceof Uint8Array) {
48+
return _;
49+
}
50+
51+
const ns = NormalizedSchema.of(schemaRef);
52+
const sparse = !!ns.getMergedTraits().sparse;
53+
54+
if (ns.isListSchema() && Array.isArray(_)) {
55+
if (!sparse) {
56+
return _.filter((item) => item != null);
57+
}
58+
} else if (_ && typeof _ === "object") {
59+
const members = ns.getMemberSchemas();
60+
const isStruct = ns.isStructSchema();
61+
if (!sparse || isStruct) {
62+
for (const [k, v] of Object.entries(_)) {
63+
const filteredOutByNonSparse = !sparse && v == null;
64+
const filteredOutByUnrecognizedMember = isStruct && !(k in members);
65+
if (filteredOutByNonSparse || filteredOutByUnrecognizedMember) {
66+
delete _[k];
67+
}
68+
}
69+
return _;
70+
}
71+
}
72+
73+
return _;
74+
});
75+
}
76+
77+
public flush(): Uint8Array {
78+
const buffer = cbor.serialize(this.value);
79+
this.value = undefined;
80+
return buffer as Uint8Array;
81+
}
82+
}
83+
84+
/**
85+
* @alpha
86+
*/
87+
export class CborShapeDeserializer implements ShapeDeserializer {
88+
private serdeContext?: SerdeFunctions;
89+
90+
public setSerdeContext(serdeContext: SerdeFunctions) {
91+
this.serdeContext = serdeContext;
92+
}
93+
94+
public read(schema: Schema, bytes: Uint8Array): any {
95+
const data: any = cbor.deserialize(bytes);
96+
return this.readValue(schema, data);
97+
}
98+
99+
private readValue(_schema: Schema, value: any): any {
100+
const ns = NormalizedSchema.of(_schema);
101+
const schema = ns.getSchema();
102+
103+
if (typeof schema === "number") {
104+
if (ns.isTimestampSchema()) {
105+
// format is ignored.
106+
return parseEpochTimestamp(value);
107+
}
108+
if (ns.isBlobSchema()) {
109+
return value;
110+
}
111+
}
112+
113+
if (
114+
typeof value === "undefined" ||
115+
typeof value === "boolean" ||
116+
typeof value === "number" ||
117+
typeof value === "string" ||
118+
typeof value === "bigint" ||
119+
typeof value === "symbol"
120+
) {
121+
return value;
122+
} else if (typeof value === "function" || typeof value === "object") {
123+
if (value === null) {
124+
return null;
125+
}
126+
if ("byteLength" in (value as Uint8Array)) {
127+
return value;
128+
}
129+
if (value instanceof Date) {
130+
return value;
131+
}
132+
if (ns.isDocumentSchema()) {
133+
return value;
134+
}
135+
136+
if (ns.isListSchema()) {
137+
const newArray = [];
138+
const memberSchema = ns.getValueSchema();
139+
const sparse = ns.isListSchema() && !!ns.getMergedTraits().sparse;
140+
141+
for (const item of value) {
142+
newArray.push(this.readValue(memberSchema, item));
143+
if (!sparse && newArray[newArray.length - 1] == null) {
144+
newArray.pop();
145+
}
146+
}
147+
return newArray;
148+
}
149+
150+
const newObject = {} as any;
151+
152+
if (ns.isMapSchema()) {
153+
const sparse = ns.getMergedTraits().sparse;
154+
const targetSchema = ns.getValueSchema();
155+
156+
for (const key of Object.keys(value)) {
157+
newObject[key] = this.readValue(targetSchema, value[key]);
158+
159+
if (newObject[key] == null && !sparse) {
160+
delete newObject[key];
161+
}
162+
}
163+
} else if (ns.isStructSchema()) {
164+
for (const [key, memberSchema] of ns.structIterator()) {
165+
newObject[key] = this.readValue(memberSchema, value[key]);
166+
}
167+
}
168+
return newObject;
169+
} else {
170+
return value;
171+
}
172+
}
173+
}

0 commit comments

Comments
 (0)