Skip to content

Commit ace910d

Browse files
committed
rework schemas
1 parent 1012326 commit ace910d

File tree

18 files changed

+629
-568
lines changed

18 files changed

+629
-568
lines changed

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

Lines changed: 25 additions & 37 deletions
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.isMapSchema()) {
6053
for (const [k, v] of Object.entries(_)) {
6154
if (v == null) {
6255
delete _[k];
@@ -88,15 +81,20 @@ export class CborShapeDeserializer implements ShapeDeserializer {
8881
return this.readValue(schema, data);
8982
}
9083

91-
private readValue(schema: Schema, value: any): any {
92-
if (typeof schema === "string") {
93-
if (schema === "time" || schema === "epoch-seconds" || schema === "date-time") {
84+
private readValue(_schema: Schema, value: any): any {
85+
const ns = NormalizedSchema.of(_schema);
86+
const schema = ns.getSchema();
87+
88+
if (typeof schema === "number") {
89+
if (ns.isTimestampSchema()) {
90+
// format is ignored.
9491
return parseEpochTimestamp(value);
9592
}
96-
if (schema === "blob" || schema === "streaming-blob") {
93+
if (ns.isBlobSchema()) {
9794
return value;
9895
}
9996
}
97+
10098
switch (typeof value) {
10199
case "undefined":
102100
case "boolean":
@@ -116,37 +114,27 @@ export class CborShapeDeserializer implements ShapeDeserializer {
116114
if (value instanceof Date) {
117115
return value;
118116
}
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;
126117

127118
if (Array.isArray(value)) {
128119
const newArray = [];
120+
const memberSchema = ns.getMemberSchema();
121+
const sparse = ns.isListSchema() && !!ns.getMergedTraits().sparse;
122+
129123
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-
}
124+
newArray.push(this.readValue(memberSchema, item));
125+
if (!sparse && newArray[newArray.length - 1] == null) {
126+
newArray.pop();
135127
}
136128
}
137129
return newArray;
138130
}
139131

140132
const newObject = {} as any;
141133
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;
134+
const targetSchema = ns.isMapSchema() ? ns.getMemberSchema() : ns.getMemberSchema(key);
148135
newObject[key] = this.readValue(targetSchema, value[key]);
149-
if (!traits?.sparse && newObject[key] == null) {
136+
137+
if (ns.isMapSchema() && newObject[key] == null && !ns.getMergedTraits().sparse) {
150138
delete newObject[key];
151139
}
152140
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,13 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
122122
} else if (memberTraits.httpPrefixHeaders) {
123123
for (const [key, val] of Object.entries(inputMember)) {
124124
const amalgam = memberTraits.httpPrefixHeaders + key;
125-
serializer.write([, { httpHeader: amalgam }], val);
125+
serializer.write([0, { httpHeader: amalgam }], val);
126126
headers[amalgam.toLowerCase()] = String(serializer.flush()).toLowerCase();
127127
}
128128
delete _input[memberName];
129129
} else if (memberTraits.httpQueryParams) {
130130
for (const [key, val] of Object.entries(inputMember)) {
131-
serializer.write([, { httpQuery: key }], val);
131+
serializer.write([0, { httpQuery: key }], val);
132132
query[key] = serializer.flush() as string;
133133
}
134134
delete _input[memberName];

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

Lines changed: 13 additions & 21 deletions
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/schemas/ErrorSchema.ts

Lines changed: 6 additions & 4 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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);

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

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,27 +63,56 @@ export class NormalizedSchema implements INormalizedSchema {
6363
}
6464

6565
public isListSchema(): boolean {
66-
return this.schema === 2 || this.schema instanceof ListSchema;
66+
if (typeof this.schema === "number") {
67+
return this.schema >> 6 === 0b01;
68+
}
69+
return this.schema instanceof ListSchema;
6770
}
6871

6972
public isMapSchema(): boolean {
70-
return this.schema === 4 || this.schema instanceof MapSchema;
73+
if (typeof this.schema === "number") {
74+
return this.schema >> 6 === 0b10;
75+
}
76+
return this.schema instanceof MapSchema;
7177
}
7278

7379
public isStructSchema(): boolean {
7480
return (
75-
this.schema === 8 ||
7681
(this.schema !== null && typeof this.schema === "object" && "members" in this.schema) ||
7782
this.schema instanceof StructureSchema
7883
);
7984
}
8085

8186
public isBlobSchema(): boolean {
82-
return this.schema === "blob" || this.schema === "streaming-blob";
87+
return this.schema === 21 || this.schema === 42;
88+
}
89+
90+
public isTimestampSchema(): boolean {
91+
return typeof this.schema === "number" && this.schema >= 4 && this.schema <= 7;
92+
}
93+
94+
public isStringSchema(): boolean {
95+
return this.schema === 0;
96+
}
97+
98+
public isBooleanSchema(): boolean {
99+
return this.schema === 2;
100+
}
101+
102+
public isNumericSchema(): boolean {
103+
return this.schema === 1;
104+
}
105+
106+
public isBigIntegerSchema(): boolean {
107+
return this.schema === 17;
108+
}
109+
110+
public isBigDecimalSchema(): boolean {
111+
return this.schema === 19;
83112
}
84113

85114
public isStreaming(): boolean {
86-
return !!this.getMergedTraits().streaming || this.getSchema() === "streaming-blob";
115+
return !!this.getMergedTraits().streaming || this.getSchema() === 42;
87116
}
88117

89118
public getMergedTraits(): SchemaTraits {
@@ -102,22 +131,31 @@ export class NormalizedSchema implements INormalizedSchema {
102131
}
103132

104133
public getMemberSchema(member?: string): NormalizedSchema {
105-
if (typeof this.schema !== "object") {
106-
return NormalizedSchema.of(void 0, member as string);
134+
if (typeof this.schema === "number" && this.schema >= 0b0010_0000) {
135+
return NormalizedSchema.of(0b0011_1111 & this.schema, member as string);
107136
}
108-
if ("members" in (this.schema as StructureSchema)) {
109-
if (typeof member === "undefined") {
110-
throw new Error("cannot call getMemberSchema(void) with StructureSchema");
137+
if (this.schema && typeof this.schema === "object") {
138+
const struct = this.schema as StructureSchema;
139+
if ("members" in struct) {
140+
if (typeof member === "undefined") {
141+
throw new Error("cannot call getMemberSchema(void) with StructureSchema");
142+
}
143+
if (!(member in struct.members)) {
144+
throw new Error(
145+
`@smithy/core/schema - structure schema has no member ${member}, members are: ${struct.memberNames.join(", ")}`
146+
);
147+
}
148+
return NormalizedSchema.of(struct.members[member], member as string);
111149
}
112-
return NormalizedSchema.of((this.schema as StructureSchema).members[member], member as string);
113-
}
114-
if ("valueSchema" in (this.schema as MapSchema | ListSchema)) {
115-
if (typeof member !== "undefined") {
116-
throw new Error("cannot call getMemberSchema(string) with List or Map Schema");
150+
const collection = this.schema as MapSchema | ListSchema;
151+
if ("valueSchema" in collection) {
152+
if (typeof member !== "undefined") {
153+
throw new Error("cannot call getMemberSchema(string) with List or Map Schema");
154+
}
155+
return NormalizedSchema.of(collection.valueSchema);
117156
}
118-
return NormalizedSchema.of((this.schema as MapSchema | ListSchema).valueSchema);
119157
}
120-
return NormalizedSchema.of(void 0, member as string);
158+
throw new Error("@smithy/core/schema - the schema does not have members.");
121159
}
122160

123161
public getMemberSchemas(): Record<string, NormalizedSchema> {

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
1-
import type { SchemaRef, SchemaTraits, StructureSchema as IStructureSchema } from "@smithy/types";
1+
import type { MemberSchema, SchemaRef, SchemaTraits, StructureSchema as IStructureSchema } from "@smithy/types";
22

33
import { TypeRegistry } from "../TypeRegistry";
44
import { Schema } from "./Schema";
55

66
export class StructureSchema extends Schema implements IStructureSchema {
7+
public members: Record<string, [SchemaRef, SchemaTraits]> = {};
8+
79
public constructor(
810
public name: string,
911
public traits: SchemaTraits,
10-
public members: Record<string, [SchemaRef, SchemaTraits]>
12+
public memberNames: string[],
13+
public memberList: SchemaRef[]
1114
) {
1215
super(name, traits);
16+
for (let i = 0; i < memberNames.length; ++i) {
17+
this.members[memberNames[i]] = Array.isArray(memberList[i])
18+
? (memberList[i] as MemberSchema)
19+
: [memberList[i], {}];
20+
}
1321
}
1422
}
1523

1624
export function struct(
1725
name: string,
1826
traits: SchemaTraits = {},
19-
members: Record<string, [SchemaRef, SchemaTraits]> = {}
27+
memberNames: string[],
28+
memberList: SchemaRef[]
2029
): StructureSchema {
21-
const schema = new StructureSchema(name, traits, members);
30+
const schema = new StructureSchema(name, traits, memberNames, memberList);
2231
if (TypeRegistry.active) {
2332
TypeRegistry.active.register(name, schema);
2433
}

packages/types/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export * from "./profile";
2222
export * from "./response";
2323
export * from "./retry";
2424
export * from "./schema/schema";
25+
export * from "./schema/sentinels";
2526
export * from "./serde";
2627
export * from "./shapes";
2728
export * from "./signature";

0 commit comments

Comments
 (0)