Skip to content

Commit c4ab269

Browse files
committed
feat(core/protocols): runtime protocol classes
1 parent 6542d25 commit c4ab269

31 files changed

+11718
-5
lines changed

clients/client-s3/src/schemas/schemas.ts

Lines changed: 9375 additions & 0 deletions
Large diffs are not rendered by default.

packages/core/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,18 @@
8282
"license": "Apache-2.0",
8383
"dependencies": {
8484
"@aws-sdk/types": "*",
85+
"@aws-sdk/xml-builder": "*",
8586
"@smithy/core": "^3.5.1",
8687
"@smithy/node-config-provider": "^4.1.3",
8788
"@smithy/property-provider": "^4.0.4",
8889
"@smithy/protocol-http": "^5.1.2",
8990
"@smithy/signature-v4": "^5.1.2",
9091
"@smithy/smithy-client": "^4.4.1",
9192
"@smithy/types": "^4.3.1",
93+
"@smithy/util-base64": "^4.0.0",
94+
"@smithy/util-body-length-browser": "^4.0.0",
9295
"@smithy/util-middleware": "^4.0.4",
96+
"@smithy/util-utf8": "^4.0.0",
9397
"fast-xml-parser": "4.4.1",
9498
"tslib": "^2.6.2"
9599
},
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ConfigurableSerdeContext, SerdeFunctions } from "@smithy/types";
2+
3+
/**
4+
* @internal
5+
*/
6+
export class SerdeContextConfig implements ConfigurableSerdeContext {
7+
protected serdeContext?: SerdeFunctions;
8+
9+
public setSerdeContext(serdeContext: SerdeFunctions): void {
10+
this.serdeContext = serdeContext;
11+
}
12+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { collectBody } from "@smithy/smithy-client";
2-
import type { HttpResponse, SerdeContext } from "@smithy/types";
2+
import type { SerdeFunctions } from "@smithy/types";
33

4-
export const collectBodyString = (streamBody: any, context: SerdeContext): Promise<string> =>
4+
export const collectBodyString = (streamBody: any, context: SerdeFunctions): Promise<string> =>
55
collectBody(streamBody, context).then((body) => context.utf8Encoder(body));
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
export * from "./coercing-serializers";
2+
export * from "./json/AwsJson1_0Protocol";
3+
export * from "./json/AwsJson1_1Protocol";
4+
export * from "./json/AwsJsonRpcProtocol";
5+
export * from "./json/AwsRestJsonProtocol";
6+
export * from "./json/JsonCodec";
7+
export * from "./json/JsonShapeDeserializer";
8+
export * from "./json/JsonShapeSerializer";
29
export * from "./json/awsExpectUnion";
310
export * from "./json/parseJsonBody";
11+
export * from "./query/AwsEc2QueryProtocol";
12+
export * from "./query/AwsQueryProtocol";
13+
export * from "./xml/AwsRestXmlProtocol";
14+
export * from "./xml/XmlCodec";
15+
export * from "./xml/XmlShapeDeserializer";
16+
export * from "./xml/XmlShapeSerializer";
417
export * from "./xml/parseXmlBody";
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { map, op, SCHEMA, sim, struct } from "@smithy/core/schema";
2+
import { toBase64 } from "@smithy/util-base64";
3+
import { toUtf8 } from "@smithy/util-utf8";
4+
import { describe, expect, test as it } from "vitest";
5+
6+
import { AwsJson1_0Protocol } from "./AwsJson1_0Protocol";
7+
8+
describe(AwsJson1_0Protocol.name, () => {
9+
const json = {
10+
string: "string",
11+
number: 1234,
12+
boolean: false,
13+
blob: "AAAAAAAAAAA=",
14+
timestamp: 0,
15+
};
16+
const schema = struct(
17+
"ns",
18+
"MyStruct",
19+
0,
20+
[...Object.keys(json)],
21+
[SCHEMA.STRING, SCHEMA.NUMERIC, SCHEMA.BOOLEAN, SCHEMA.BLOB, SCHEMA.TIMESTAMP_DEFAULT]
22+
);
23+
const serdeContext = {
24+
base64Encoder: toBase64,
25+
utf8Encoder: toUtf8,
26+
} as any;
27+
28+
describe("codec", () => {
29+
it("serializes blobs and timestamps", () => {
30+
const protocol = new AwsJson1_0Protocol({
31+
defaultNamespace: "namespace",
32+
});
33+
protocol.setSerdeContext(serdeContext);
34+
const codec = protocol.getPayloadCodec();
35+
const serializer = codec.createSerializer();
36+
const data = {
37+
string: "string",
38+
number: 1234,
39+
boolean: false,
40+
blob: new Uint8Array(8),
41+
timestamp: new Date(0),
42+
};
43+
serializer.write(schema, data);
44+
const serialized = serializer.flush();
45+
expect(JSON.parse(serialized)).toEqual({
46+
string: "string",
47+
number: 1234,
48+
boolean: false,
49+
blob: "AAAAAAAAAAA=",
50+
timestamp: 0,
51+
});
52+
});
53+
54+
it("deserializes blobs and timestamps", async () => {
55+
const protocol = new AwsJson1_0Protocol({
56+
defaultNamespace: "namespace",
57+
});
58+
protocol.setSerdeContext(serdeContext);
59+
const codec = protocol.getPayloadCodec();
60+
const deserializer = codec.createDeserializer();
61+
62+
const parsed = await deserializer.read(schema, JSON.stringify(json));
63+
expect(parsed).toEqual({
64+
string: "string",
65+
number: 1234,
66+
boolean: false,
67+
blob: new Uint8Array(8),
68+
timestamp: new Date(0),
69+
});
70+
});
71+
72+
it("ignores JSON name and HTTP bindings", async () => {
73+
const protocol = new AwsJson1_0Protocol({
74+
defaultNamespace: "namespace",
75+
});
76+
protocol.setSerdeContext(serdeContext);
77+
78+
const schema = struct(
79+
"ns",
80+
"MyHttpBindingStructure",
81+
{},
82+
["header", "query", "headerMap", "payload"],
83+
[
84+
sim("ns", "MyHeader", SCHEMA.STRING, { httpHeader: "header", jsonName: "MyHeader" }),
85+
sim("ns", "MyQuery", SCHEMA.STRING, { httpQuery: "query" }),
86+
map(
87+
"ns",
88+
"HeaderMap",
89+
{
90+
httpPrefixHeaders: "",
91+
},
92+
SCHEMA.STRING,
93+
SCHEMA.NUMERIC
94+
),
95+
sim("ns", "MyPayload", SCHEMA.DOCUMENT, { httpPayload: 1 }),
96+
]
97+
);
98+
const operationSchema = op("ns", "MyOperation", {}, schema, "unit");
99+
100+
const request = await protocol.serializeRequest(
101+
operationSchema,
102+
{
103+
header: "hello",
104+
query: "world",
105+
headerMap: {
106+
a: 1,
107+
b: 2,
108+
},
109+
},
110+
{
111+
async endpoint() {
112+
return {
113+
protocol: "https:",
114+
hostname: "amazonaws.com",
115+
path: "/",
116+
};
117+
},
118+
} as any
119+
);
120+
121+
expect(request.headers).toEqual({
122+
"content-length": "60",
123+
"content-type": "application/x-amz-json-1.0",
124+
"x-amz-target": "JsonRpc10.MyOperation",
125+
});
126+
expect(request.query).toEqual({});
127+
expect(request.body).toEqual(`{"header":"hello","query":"world","headerMap":{"a":1,"b":2}}`);
128+
});
129+
});
130+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { AwsJsonRpcProtocol } from "./AwsJsonRpcProtocol";
2+
3+
/**
4+
* @alpha
5+
* @see https://smithy.io/2.0/aws/protocols/aws-json-1_1-protocol.html#differences-between-awsjson1-0-and-awsjson1-1
6+
*/
7+
export class AwsJson1_0Protocol extends AwsJsonRpcProtocol {
8+
public constructor({ defaultNamespace }: { defaultNamespace: string }) {
9+
super({
10+
defaultNamespace,
11+
});
12+
}
13+
14+
public getShapeId(): string {
15+
return "aws.protocols#awsJson1_0";
16+
}
17+
18+
protected getJsonRpcVersion() {
19+
return "1.0" as const;
20+
}
21+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { describe, expect, test as it } from "vitest";
2+
3+
describe("", () => {
4+
it("placeholder", async () => {
5+
void expect;
6+
throw new Error("NYI");
7+
});
8+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { AwsJsonRpcProtocol } from "./AwsJsonRpcProtocol";
2+
3+
/**
4+
* @alpha
5+
* @see https://smithy.io/2.0/aws/protocols/aws-json-1_1-protocol.html#differences-between-awsjson1-0-and-awsjson1-1
6+
*/
7+
export class AwsJson1_1Protocol extends AwsJsonRpcProtocol {
8+
public constructor({ defaultNamespace }: { defaultNamespace: string }) {
9+
super({
10+
defaultNamespace,
11+
});
12+
}
13+
14+
public getShapeId(): string {
15+
return "aws.protocols#awsJson1_1";
16+
}
17+
18+
protected getJsonRpcVersion() {
19+
return "1.1" as const;
20+
}
21+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { describe, expect, test as it } from "vitest";
2+
3+
describe("", () => {
4+
it("placeholder", async () => {
5+
void expect;
6+
throw new Error("NYI");
7+
});
8+
});

0 commit comments

Comments
 (0)