Skip to content

Commit 8c14d3d

Browse files
committed
rework schemas
1 parent 2177307 commit 8c14d3d

22 files changed

+773
-570
lines changed

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

+36-39
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";
@@ -46,17 +37,19 @@ export class CborShapeSerializer implements ShapeSerializer<Uint8Array> {
4637
if (_ instanceof Date) {
4738
return dateToTag(_);
4839
}
49-
const schema = deref((schemaRef as MemberSchema)?.[0] ?? schemaRef);
5040
if (_ instanceof Uint8Array) {
5141
return _;
5242
}
53-
const sparse = (schema as TraitsSchema)?.traits?.sparse;
43+
44+
const ns = NormalizedSchema.of(schemaRef);
45+
const sparse = !!ns.getMergedTraits().sparse;
46+
5447
if (Array.isArray(_)) {
5548
if (!sparse) {
5649
return _.filter((item) => item != null);
5750
}
5851
} else if (_ && typeof _ === "object") {
59-
if (!sparse) {
52+
if (!sparse || ns.isStructSchema()) {
6053
for (const [k, v] of Object.entries(_)) {
6154
if (v == null) {
6255
delete _[k];
@@ -65,6 +58,7 @@ export class CborShapeSerializer implements ShapeSerializer<Uint8Array> {
6558
return _;
6659
}
6760
}
61+
6862
return _;
6963
});
7064
}
@@ -88,15 +82,20 @@ export class CborShapeDeserializer implements ShapeDeserializer {
8882
return this.readValue(schema, data);
8983
}
9084

91-
private readValue(schema: Schema, value: any): any {
92-
if (typeof schema === "string") {
93-
if (schema === "time" || schema === "epoch-seconds" || schema === "date-time") {
85+
private readValue(_schema: Schema, value: any): any {
86+
const ns = NormalizedSchema.of(_schema);
87+
const schema = ns.getSchema();
88+
89+
if (typeof schema === "number") {
90+
if (ns.isTimestampSchema()) {
91+
// format is ignored.
9492
return parseEpochTimestamp(value);
9593
}
96-
if (schema === "blob" || schema === "streaming-blob") {
94+
if (ns.isBlobSchema()) {
9795
return value;
9896
}
9997
}
98+
10099
switch (typeof value) {
101100
case "undefined":
102101
case "boolean":
@@ -116,38 +115,36 @@ export class CborShapeDeserializer implements ShapeDeserializer {
116115
if (value instanceof Date) {
117116
return value;
118117
}
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;
126118

127119
if (Array.isArray(value)) {
128120
const newArray = [];
121+
const memberSchema = ns.getValueSchema();
122+
const sparse = ns.isListSchema() && !!ns.getMergedTraits().sparse;
123+
129124
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-
}
125+
newArray.push(this.readValue(memberSchema, item));
126+
if (!sparse && newArray[newArray.length - 1] == null) {
127+
newArray.pop();
135128
}
136129
}
137130
return newArray;
138131
}
139132

140133
const newObject = {} as any;
141134
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];
135+
if (ns.isMapSchema() || ns.isDocumentSchema()) {
136+
const targetSchema = ns.getValueSchema();
137+
newObject[key] = this.readValue(targetSchema, value[key]);
138+
139+
if (ns.isMapSchema() && newObject[key] == null && !ns.getMergedTraits().sparse) {
140+
delete newObject[key];
141+
}
142+
} else if (ns.isStructSchema()) {
143+
const targetSchema = ns.getMemberSchema(key);
144+
if (targetSchema === undefined) {
145+
continue;
146+
}
147+
newObject[key] = this.readValue(targetSchema, value[key]);
151148
}
152149
}
153150
return newObject;

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,10 @@ describe(SmithyRpcV2CborProtocol.name, () => {
154154
const protocol = new SmithyRpcV2CborProtocol();
155155
const httpRequest = await protocol.serializeRequest(
156156
{
157+
name: "dummy",
157158
input: testCase.schema,
158159
output: void 0,
159160
traits: {},
160-
errors: [],
161161
},
162162
testCase.input,
163163
{
@@ -249,10 +249,10 @@ describe(SmithyRpcV2CborProtocol.name, () => {
249249
const protocol = new SmithyRpcV2CborProtocol();
250250
const output = await protocol.deserializeResponse(
251251
{
252+
name: "dummy",
252253
input: void 0,
253254
output: testCase.schema,
254255
traits: {},
255-
errors: [],
256256
},
257257
{},
258258
new HttpResponse({

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HttpInterceptingShapeSerializer,HttpProtocol } from "@smithy/core/protocols";
1+
import { HttpInterceptingShapeSerializer, HttpProtocol } from "@smithy/core/protocols";
22
import { deref, ErrorSchema, OperationSchema, TypeRegistry } from "@smithy/core/schema";
33
import type {
44
HandlerExecutionContext,
@@ -36,6 +36,10 @@ export class SmithyRpcV2CborProtocol extends HttpProtocol {
3636
delete request.body;
3737
delete request.headers["content-type"];
3838
} else {
39+
if (!request.body) {
40+
this.serializer.write(15, {})
41+
request.body = this.serializer.flush();
42+
}
3943
try {
4044
request.headers["content-length"] = String((request.body as Uint8Array).byteLength);
4145
} catch (e) {}
@@ -82,6 +86,7 @@ export class SmithyRpcV2CborProtocol extends HttpProtocol {
8286
// TODO(schema) throw client base exception using the dataObject.
8387
throw new Error("schema not found for " + error);
8488
}
89+
8590
const message = dataObject.message ?? dataObject.Message ?? "Unknown";
8691
const exception = new errorSchema.ctor(message);
8792
Object.assign(exception, {

packages/core/src/submodules/protocols/HttpProtocol.ts

+18-6
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
8787

8888
for (const memberName of Object.keys(_input)) {
8989
const memberNs = ns.getMemberSchema(memberName);
90+
if (memberNs === undefined) {
91+
continue;
92+
}
9093
const memberSchema = memberNs.getSchema();
9194
const memberTraits = memberNs.getMergedTraits();
9295
const inputMember = (_input as any)[memberName] as any;
@@ -122,13 +125,13 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
122125
} else if (memberTraits.httpPrefixHeaders) {
123126
for (const [key, val] of Object.entries(inputMember)) {
124127
const amalgam = memberTraits.httpPrefixHeaders + key;
125-
serializer.write([, { httpHeader: amalgam }], val);
128+
serializer.write([0, { httpHeader: amalgam }], val);
126129
headers[amalgam.toLowerCase()] = String(serializer.flush()).toLowerCase();
127130
}
128131
delete _input[memberName];
129132
} else if (memberTraits.httpQueryParams) {
130133
for (const [key, val] of Object.entries(inputMember)) {
131-
serializer.write([, { httpQuery: key }], val);
134+
serializer.write([0, { httpQuery: key }], val);
132135
query[key] = serializer.flush() as string;
133136
}
134137
delete _input[memberName];
@@ -178,6 +181,16 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
178181
const schema = ns.getSchema() as StructureSchema;
179182

180183
let dataObject: any = {};
184+
185+
if (response.statusCode >= 300) {
186+
const bytes: Uint8Array = await collectBody(response.body, context as SerdeContext);
187+
if (bytes.byteLength > 0) {
188+
Object.assign(dataObject, await deserializer.read(15, bytes));
189+
}
190+
await this.handleError(operationSchema, context, response, dataObject, this.deserializeMetadata(response));
191+
throw new Error("@smithy/core/protocols - HTTP Protocol error handler failed to throw.");
192+
}
193+
181194
let hasNonHttpBindingMember = false;
182195

183196
for (const header in response.headers) {
@@ -188,6 +201,9 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
188201

189202
for (const [memberName] of Object.entries(schema?.members ?? {})) {
190203
const memberSchema = ns.getMemberSchema(memberName);
204+
if (memberSchema === undefined) {
205+
continue;
206+
}
191207
const memberSchemas = memberSchema.getMemberSchemas();
192208
const memberTraits = memberSchema.getMemberTraits();
193209

@@ -261,10 +277,6 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
261277
}
262278
}
263279

264-
if (response.statusCode >= 300) {
265-
await this.handleError(operationSchema, context, response, dataObject, this.deserializeMetadata(response));
266-
}
267-
268280
const output: Output = {
269281
$metadata: this.deserializeMetadata(response),
270282
...dataObject,

packages/core/src/submodules/schema/TypeRegistry.ts

+13-21
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,13 @@ export class TypeRegistry {
2929
* @param schema - to be registered.
3030
*/
3131
public register(shapeId: string, schema: ISchema) {
32-
const qualifiedName = this.namespace + "#" + shapeId;
32+
const qualifiedName = this.normalizeShapeId(shapeId);
3333
this.schemas.set(qualifiedName, schema);
3434
if (typeof schema === "object") {
3535
TypeRegistry.schemaToRegistry.set(schema, this);
3636
}
3737
}
3838

39-
/**
40-
* Used to disambiguate e.g. XML values, which are all strings,
41-
* into other simple JavaScript types like boolean and number.
42-
*
43-
* @param simpleTypes - map of shape id strings to simple type.
44-
*/
45-
public registerSimpleTypes(simpleTypes: Record<string, "boolean" | "number" | "bigint" | "bigdecimal">): void {
46-
for (const [name, type] of Object.entries(simpleTypes)) {
47-
const normalizedName = name.includes("#") ? name : this.namespace + "#" + name;
48-
this.simpleTypes[normalizedName] = type;
49-
}
50-
}
51-
5239
/**
5340
* Used to disambiguate e.g. XML values, which are all strings,
5441
* into other simple JavaScript types like boolean and number.
@@ -57,21 +44,19 @@ export class TypeRegistry {
5744
* @returns simple type of the shape id in this registry.
5845
*/
5946
public getSimpleType(shapeId: string): string {
60-
if (shapeId.includes("#")) {
61-
return this.simpleTypes[shapeId];
62-
}
63-
return this.simpleTypes[this.namespace + "#" + shapeId] ?? "unknown";
47+
return this.simpleTypes[this.normalizeShapeId(shapeId)] ?? "unknown";
6448
}
6549

6650
/**
6751
* @param shapeId - query.
6852
* @returns the schema.
6953
*/
7054
public getSchema(shapeId: string): ISchema {
71-
if (shapeId.includes("#")) {
72-
return this.schemas.get(shapeId);
55+
const id = this.normalizeShapeId(shapeId);
56+
if (!this.schemas.has(id)) {
57+
throw new Error(`@smithy/core/schema - schema not found for ${id}`);
7358
}
74-
return this.schemas.get(this.namespace + "#" + shapeId);
59+
return this.schemas.get(id)!;
7560
}
7661

7762
/**
@@ -93,4 +78,11 @@ export class TypeRegistry {
9378
TypeRegistry.registries.delete(this.namespace);
9479
this.schemas.clear();
9580
}
81+
82+
private normalizeShapeId(shapeId: string) {
83+
if (shapeId.includes("#")) {
84+
return shapeId;
85+
}
86+
return this.namespace + "#" + shapeId;
87+
}
9688
}

packages/core/src/submodules/schema/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ export * from "./schemas/OperationSchema";
66
export * from "./schemas/ErrorSchema";
77
export * from "./schemas/NormalizedSchema";
88
export * from "./schemas/Schema";
9+
export * from "./schemas/SimpleSchema";
910
export * from "./schemas/StructureSchema";
1011
export * from "./TypeRegistry";

packages/core/src/submodules/schema/schemas/ErrorSchema.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,25 @@ export class ErrorSchema extends StructureSchema {
77
public constructor(
88
public name: string,
99
public traits: SchemaTraits,
10-
public members: Record<string, [SchemaRef, SchemaTraits]>,
10+
public memberNames: string[],
11+
public memberList: SchemaRef[],
1112
/**
1213
* Constructor for a modeled service exception class that extends Error.
1314
*/
1415
public ctor: any
1516
) {
16-
super(name, traits, members);
17+
super(name, traits, memberNames, memberList);
1718
}
1819
}
1920

2021
export function error(
2122
name: string,
2223
traits: SchemaTraits = {},
23-
members: Record<string, [SchemaRef, SchemaTraits]> = {},
24+
memberNames: string[],
25+
memberList: SchemaRef[],
2426
ctor: any
2527
): ErrorSchema {
26-
const schema = new ErrorSchema(name, traits, members, ctor);
28+
const schema = new ErrorSchema(name, traits, memberNames, memberList, ctor);
2729
if (TypeRegistry.active) {
2830
TypeRegistry.active.register(name, schema);
2931
}

packages/core/src/submodules/schema/schemas/ListSchema.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class ListSchema extends Schema implements IListSchema {
1313
}
1414
}
1515

16-
export function list(name: string, traits: SchemaTraits = {}, valueSchema: SchemaRef = void 0): ListSchema {
16+
export function list(name: string, traits: SchemaTraits = {}, valueSchema: SchemaRef): ListSchema {
1717
const schema = new ListSchema(name, traits, typeof valueSchema === "function" ? valueSchema() : valueSchema);
1818
if (TypeRegistry.active) {
1919
TypeRegistry.active.register(name, schema);

packages/core/src/submodules/schema/schemas/MapSchema.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class MapSchema extends Schema implements IMapSchema {
1313
}
1414
}
1515

16-
export function map(name: string, traits: SchemaTraits = {}, valueSchema: SchemaRef = void 0): MapSchema {
16+
export function map(name: string, traits: SchemaTraits = {}, valueSchema: SchemaRef): MapSchema {
1717
const schema = new MapSchema(name, traits, typeof valueSchema === "function" ? valueSchema() : valueSchema);
1818
if (TypeRegistry.active) {
1919
TypeRegistry.active.register(name, schema);

0 commit comments

Comments
 (0)