Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/pink-items-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@smithy/types": minor
"@smithy/core": minor
---

schema code size optimizations
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ describe(SmithyRpcV2CborProtocol.name, () => {
const protocol = new SmithyRpcV2CborProtocol({ defaultNamespace: "" });
const httpRequest = await protocol.serializeRequest(
{
namespace: "ns",
name: "dummy",
input: testCase.schema,
output: "unit",
Expand Down Expand Up @@ -256,6 +257,7 @@ describe(SmithyRpcV2CborProtocol.name, () => {
});
const output = await protocol.deserializeResponse(
{
namespace: "ns",
name: "dummy",
input: "unit",
output: testCase.schema,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { RpcProtocol } from "@smithy/core/protocols";
import type { ErrorSchema, OperationSchema } from "@smithy/core/schema";
import type { ErrorSchema } from "@smithy/core/schema";
import { deref, NormalizedSchema, TypeRegistry } from "@smithy/core/schema";
import type {
EndpointBearer,
HandlerExecutionContext,
HttpRequest as IHttpRequest,
HttpResponse as IHttpResponse,
MetadataBearer,
OperationSchema,
ResponseMetadata,
SerdeFunctions,
} from "@smithy/types";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe(HttpBindingProtocol.name, () => {
const response = new HttpResponse({
statusCode: 200,
headers: {
"x-timestamplist": "Mon, 16 Dec 2019 23:48:18 GMT, Mon, 16 Dec 2019 23:48:18 GMT",
"x-timestamplist": "Mon, 16 Nov 2019 23:48:18 GMT, Mon, 16 Dec 2019 23:48:18 GMT",
},
});

Expand Down Expand Up @@ -95,7 +95,7 @@ describe(HttpBindingProtocol.name, () => {
)) as Partial<MetadataBearer>;
delete output.$metadata;
expect(output).toEqual({
timestampList: [new Date("2019-12-16T23:48:18.000Z"), new Date("2019-12-16T23:48:18.000Z")],
timestampList: [new Date("2019-11-16T23:48:18.000Z"), new Date("2019-12-16T23:48:18.000Z")],
});
});

Expand Down
30 changes: 14 additions & 16 deletions packages/core/src/submodules/protocols/HttpBindingProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,20 +157,15 @@ export abstract class HttpBindingProtocol extends HttpProtocol {
if (traits.httpQueryParams) {
for (const [key, val] of Object.entries(data)) {
if (!(key in query)) {
this.serializeQuery(
NormalizedSchema.of([
ns.getValueSchema(),
{
// We pass on the traits to the sub-schema
// because we are still in the process of serializing the map itself.
...traits,
httpQuery: key,
httpQueryParams: undefined,
},
]),
val,
query
);
const valueSchema = ns.getValueSchema();
Object.assign(valueSchema.getMergedTraits(), {
// We pass on the traits to the sub-schema
// because we are still in the process of serializing the map itself.
...traits,
httpQuery: key,
httpQueryParams: undefined,
});
this.serializeQuery(valueSchema, val, query);
}
}
return;
Expand Down Expand Up @@ -305,6 +300,7 @@ export abstract class HttpBindingProtocol extends HttpProtocol {
if (null != value) {
if (memberSchema.isListSchema()) {
const headerListValueSchema = memberSchema.getValueSchema();
headerListValueSchema.getMergedTraits().httpHeader = key;
let sections: string[];
if (
headerListValueSchema.isTimestampSchema() &&
Expand All @@ -316,7 +312,7 @@ export abstract class HttpBindingProtocol extends HttpProtocol {
}
const list = [];
for (const section of sections) {
list.push(await deserializer.read([headerListValueSchema, { httpHeader: key }], section.trim()));
list.push(await deserializer.read(headerListValueSchema, section.trim()));
}
dataObject[memberName] = list;
} else {
Expand All @@ -327,8 +323,10 @@ export abstract class HttpBindingProtocol extends HttpProtocol {
dataObject[memberName] = {};
for (const [header, value] of Object.entries(response.headers)) {
if (header.startsWith(memberTraits.httpPrefixHeaders)) {
const valueSchema = memberSchema.getValueSchema();
valueSchema.getMergedTraits().httpHeader = header;
dataObject[memberName][header.slice(memberTraits.httpPrefixHeaders.length)] = await deserializer.read(
[memberSchema.getValueSchema(), { httpHeader: header }],
valueSchema,
value
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,18 @@ export class FromStringShapeDeserializer implements ShapeDeserializer<string> {

public read(_schema: Schema, data: string): any {
const ns = NormalizedSchema.of(_schema);

if (ns.isListSchema()) {
return splitHeader(data).map((item) => this.read(ns.getValueSchema(), item));
}

if (ns.isBlobSchema()) {
return (this.serdeContext?.base64Decoder ?? fromBase64)(data);
}

if (ns.isTimestampSchema()) {
const format = determineTimestampFormat(ns, this.settings);

switch (format) {
case SCHEMA.TIMESTAMP_DATE_TIME:
return parseRfc3339DateTimeWithOffset(data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function determineTimestampFormat(
}

const { httpLabel, httpPrefixHeaders, httpHeader, httpQuery } = ns.getMergedTraits();

const bindingFormat = settings.httpBindings
? typeof httpPrefixHeaders === "string" || Boolean(httpHeader)
? SCHEMA.TIMESTAMP_HTTP_DATE
Expand Down
46 changes: 15 additions & 31 deletions packages/core/src/submodules/schema/schemas/ErrorSchema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { SchemaRef, SchemaTraits } from "@smithy/types";

import { TypeRegistry } from "../TypeRegistry";
import { Schema } from "./Schema";
import { StructureSchema } from "./StructureSchema";

/**
Expand All @@ -12,30 +13,9 @@ import { StructureSchema } from "./StructureSchema";
* @alpha
*/
export class ErrorSchema extends StructureSchema {
public static symbol = Symbol.for("@smithy/core/schema::ErrorSchema");
protected symbol = ErrorSchema.symbol;

public constructor(
public name: string,
public traits: SchemaTraits,
public memberNames: string[],
public memberList: SchemaRef[],
/**
* Constructor for a modeled service exception class that extends Error.
*/
public ctor: any
) {
super(name, traits, memberNames, memberList);
}

public static [Symbol.hasInstance](lhs: unknown): lhs is ErrorSchema {
const isPrototype = ErrorSchema.prototype.isPrototypeOf(lhs as any);
if (!isPrototype && typeof lhs === "object" && lhs !== null) {
const err = lhs as ErrorSchema;
return err.symbol === ErrorSchema.symbol;
}
return isPrototype;
}
public static readonly symbol = Symbol.for("@smithy/err");
public ctor!: any;
protected readonly symbol = ErrorSchema.symbol;
}

/**
Expand All @@ -50,15 +30,19 @@ export class ErrorSchema extends StructureSchema {
* @param memberList - list of schemaRef corresponding to each
* @param ctor - class reference for the existing Error extending class.
*/
export function error(
export const error = (
namespace: string,
name: string,
traits: SchemaTraits = {},
traits: SchemaTraits,
memberNames: string[],
memberList: SchemaRef[],
ctor: any
): ErrorSchema {
const schema = new ErrorSchema(namespace + "#" + name, traits, memberNames, memberList, ctor);
TypeRegistry.for(namespace).register(name, schema);
return schema;
}
): ErrorSchema =>
Schema.assign(new ErrorSchema(), {
name,
namespace,
traits,
memberNames,
memberList,
ctor,
});
39 changes: 11 additions & 28 deletions packages/core/src/submodules/schema/schemas/ListSchema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { ListSchema as IListSchema, SchemaRef, SchemaTraits } from "@smithy/types";

import { TypeRegistry } from "../TypeRegistry";
import { Schema } from "./Schema";

/**
Expand All @@ -10,38 +9,22 @@ import { Schema } from "./Schema";
* @alpha
*/
export class ListSchema extends Schema implements IListSchema {
public static symbol = Symbol.for("@smithy/core/schema::ListSchema");
protected symbol = ListSchema.symbol;

public constructor(
public name: string,
public traits: SchemaTraits,
public valueSchema: SchemaRef
) {
super(name, traits);
}

public static [Symbol.hasInstance](lhs: unknown): lhs is ListSchema {
const isPrototype = ListSchema.prototype.isPrototypeOf(lhs as any);
if (!isPrototype && typeof lhs === "object" && lhs !== null) {
const list = lhs as ListSchema;
return list.symbol === ListSchema.symbol;
}
return isPrototype;
}
public static readonly symbol = Symbol.for("@smithy/lis");
public name!: string;
public traits!: SchemaTraits;
public valueSchema!: SchemaRef;
protected readonly symbol = ListSchema.symbol;
}

/**
* Factory for ListSchema.
*
* @internal
*/
export function list(namespace: string, name: string, traits: SchemaTraits = {}, valueSchema: SchemaRef): ListSchema {
const schema = new ListSchema(
namespace + "#" + name,
export const list = (namespace: string, name: string, traits: SchemaTraits, valueSchema: SchemaRef): ListSchema =>
Schema.assign(new ListSchema(), {
name,
namespace,
traits,
typeof valueSchema === "function" ? valueSchema() : valueSchema
);
TypeRegistry.for(namespace).register(name, schema);
return schema;
}
valueSchema,
});
51 changes: 17 additions & 34 deletions packages/core/src/submodules/schema/schemas/MapSchema.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,38 @@
import type { MapSchema as IMapSchema, SchemaRef, SchemaTraits } from "@smithy/types";

import { TypeRegistry } from "../TypeRegistry";
import { Schema } from "./Schema";

/**
* A schema with a key schema and value schema.
* @alpha
*/
export class MapSchema extends Schema implements IMapSchema {
public static symbol = Symbol.for("@smithy/core/schema::MapSchema");
protected symbol = MapSchema.symbol;

public constructor(
public name: string,
public traits: SchemaTraits,
/**
* This is expected to be StringSchema, but may have traits.
*/
public keySchema: SchemaRef,
public valueSchema: SchemaRef
) {
super(name, traits);
}

public static [Symbol.hasInstance](lhs: unknown): lhs is MapSchema {
const isPrototype = MapSchema.prototype.isPrototypeOf(lhs as any);
if (!isPrototype && typeof lhs === "object" && lhs !== null) {
const map = lhs as MapSchema;
return map.symbol === MapSchema.symbol;
}
return isPrototype;
}
public static readonly symbol = Symbol.for("@smithy/map");
public name!: string;
public traits!: SchemaTraits;
/**
* This is expected to be StringSchema, but may have traits.
*/
public keySchema!: SchemaRef;
public valueSchema!: SchemaRef;
protected readonly symbol = MapSchema.symbol;
}

/**
* Factory for MapSchema.
* @internal
*/
export function map(
export const map = (
namespace: string,
name: string,
traits: SchemaTraits = {},
traits: SchemaTraits,
keySchema: SchemaRef,
valueSchema: SchemaRef
): MapSchema {
const schema = new MapSchema(
namespace + "#" + name,
): MapSchema =>
Schema.assign(new MapSchema(), {
name,
namespace,
traits,
keySchema,
typeof valueSchema === "function" ? valueSchema() : valueSchema
);
TypeRegistry.for(namespace).register(name, schema);
return schema;
}
valueSchema,
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { MemberSchema } from "@smithy/types";
import type { MemberSchema, StructureSchema } from "@smithy/types";
import { describe, expect, test as it } from "vitest";

import { list } from "./ListSchema";
Expand All @@ -12,8 +12,8 @@ describe(NormalizedSchema.name, () => {
const [List, Map, Struct] = [list("ack", "List", { sparse: 1 }, 0), map("ack", "Map", 0, 0, 1), () => schema];
const schema = struct("ack", "Structure", {}, ["list", "map", "struct"], [List, Map, Struct]);

const ns = new NormalizedSchema(() => schema);
const nsFromIndirect = NormalizedSchema.of([() => ns, 0]);
const ns = NormalizedSchema.of(schema);
const nsFromIndirect = NormalizedSchema.of(() => ns);

it("has a static constructor", () => {
expect(NormalizedSchema.of(ns)).toBeInstanceOf(NormalizedSchema);
Expand Down Expand Up @@ -105,7 +105,15 @@ describe(NormalizedSchema.name, () => {
expect(NormalizedSchema.of(SCHEMA.BIG_INTEGER).isBigIntegerSchema()).toBe(true);
expect(NormalizedSchema.of(SCHEMA.BIG_DECIMAL).isBigDecimalSchema()).toBe(true);
expect(NormalizedSchema.of(SCHEMA.STREAMING_BLOB).isStreaming()).toBe(true);
expect(NormalizedSchema.of([ns, { streaming: 1 }]).isStreaming()).toBe(true);

const structWithStreamingMember = struct(
"ack",
"StructWithStreamingMember",
0,
["m"],
[sim("ns", "blob", SCHEMA.BLOB, { streaming: 1 })]
);
expect(NormalizedSchema.of(structWithStreamingMember).getMemberSchema("m").isStreaming()).toBe(true);
});

describe("list member", () => {
Expand Down Expand Up @@ -184,8 +192,8 @@ describe(NormalizedSchema.name, () => {

describe("iteration", () => {
it("iterates over member schemas", () => {
const iteration = Array.from(ns.structIterator());
const entries = Object.entries(ns.getMemberSchemas());
const iteration = Array.from(ns.structIterator()) as [string, NormalizedSchema][];
const entries = Object.entries(ns.getMemberSchemas()) as [string, NormalizedSchema][];
for (let i = 0; i < iteration.length; i++) {
const [name, schema] = iteration[i];
const [entryName, entrySchema] = entries[i];
Expand All @@ -203,7 +211,9 @@ describe(NormalizedSchema.name, () => {

describe("traits", () => {
const member: MemberSchema = [sim("ack", "SimpleString", 0, { idempotencyToken: 1 }), 0b0000_0001];
const ns = NormalizedSchema.of(member, "member_name");
const container: StructureSchema = struct("ack", "Container", 0, ["member_name"], [member, 0]);

const ns = NormalizedSchema.of(container).getMemberSchema("member_name");

it("has merged traits", () => {
expect(ns.getMergedTraits()).toEqual({
Expand Down
Loading
Loading