Skip to content

Commit 7f8af58

Browse files
authored
feat: generate static schema (#1739)
1 parent 8a2873c commit 7f8af58

31 files changed

+657
-585
lines changed

.changeset/lazy-brooms-sip.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@smithy/smithy-client": minor
3+
"@smithy/types": minor
4+
"@smithy/core": minor
5+
---
6+
7+
generation of static schema

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Schema as ISchema } from "@smithy/types";
1+
import type { Schema as ISchema, StaticErrorSchema } from "@smithy/types";
22

33
import type { ErrorSchema } from "./schemas/ErrorSchema";
44

@@ -13,7 +13,7 @@ export class TypeRegistry {
1313
private constructor(
1414
public readonly namespace: string,
1515
private schemas: Map<string, ISchema> = new Map(),
16-
private exceptions: Map<ErrorSchema, any> = new Map()
16+
private exceptions: Map<ErrorSchema | StaticErrorSchema, any> = new Map()
1717
) {}
1818

1919
/**
@@ -53,16 +53,16 @@ export class TypeRegistry {
5353
/**
5454
* Associates an error schema with its constructor.
5555
*/
56-
public registerError(errorSchema: ErrorSchema, ctor: any) {
57-
this.exceptions.set(errorSchema, ctor);
56+
public registerError(es: ErrorSchema | StaticErrorSchema, ctor: any) {
57+
this.exceptions.set(es, ctor);
5858
}
5959

6060
/**
61-
* @param errorSchema - query.
61+
* @param es - query.
6262
* @returns Error constructor that extends the service's base exception.
6363
*/
64-
public getErrorCtor(errorSchema: ErrorSchema): any {
65-
return this.exceptions.get(errorSchema);
64+
public getErrorCtor(es: ErrorSchema | StaticErrorSchema): any {
65+
return this.exceptions.get(es);
6666
}
6767

6868
/**

packages/core/src/submodules/schema/middleware/schemaDeserializationMiddleware.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import type {
55
HandlerExecutionContext,
66
MetadataBearer,
77
OperationSchema,
8+
StaticOperationSchema,
89
} from "@smithy/types";
910
import { getSmithyContext } from "@smithy/util-middleware";
1011

12+
import { hydrate, isStaticSchema } from "../schemas/NormalizedSchema";
1113
import type { PreviouslyResolved } from "./schema-middleware-types";
1214

1315
/**
@@ -18,9 +20,13 @@ export const schemaDeserializationMiddleware =
1820
(next: DeserializeHandler<any, any>, context: HandlerExecutionContext) =>
1921
async (args: DeserializeHandlerArguments<any>) => {
2022
const { response } = await next(args);
21-
const { operationSchema } = getSmithyContext(context) as {
22-
operationSchema: OperationSchema;
23+
let { operationSchema } = getSmithyContext(context) as {
24+
operationSchema: OperationSchema | StaticOperationSchema;
2325
};
26+
if (isStaticSchema(operationSchema)) {
27+
operationSchema = hydrate(operationSchema);
28+
}
29+
2430
try {
2531
const parsed = await config.protocol.deserializeResponse(
2632
operationSchema,

packages/core/src/submodules/schema/middleware/schemaSerializationMiddleware.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import type {
66
Provider,
77
SerializeHandler,
88
SerializeHandlerArguments,
9+
StaticOperationSchema,
910
} from "@smithy/types";
1011
import { getSmithyContext } from "@smithy/util-middleware";
1112

13+
import { hydrate, isStaticSchema } from "../schemas/NormalizedSchema";
1214
import type { PreviouslyResolved } from "./schema-middleware-types";
1315

1416
/**
@@ -18,9 +20,12 @@ export const schemaSerializationMiddleware =
1820
(config: PreviouslyResolved) =>
1921
(next: SerializeHandler<any, any>, context: HandlerExecutionContext) =>
2022
async (args: SerializeHandlerArguments<any>) => {
21-
const { operationSchema } = getSmithyContext(context) as {
22-
operationSchema: IOperationSchema;
23+
let { operationSchema } = getSmithyContext(context) as {
24+
operationSchema: IOperationSchema | StaticOperationSchema;
2325
};
26+
if (isStaticSchema(operationSchema)) {
27+
operationSchema = hydrate(operationSchema);
28+
}
2429

2530
const endpoint: Provider<Endpoint> =
2631
context.endpointV2?.url && config.urlParser

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import type {
88
MapSchemaModifier,
99
MemberSchema,
1010
NumericSchema,
11+
StaticListSchema,
12+
StaticMapSchema,
13+
StaticStructureSchema,
1114
StreamingBlobSchema,
1215
StringSchema,
1316
StructureSchema,
@@ -313,4 +316,32 @@ describe(NormalizedSchema.name, () => {
313316
expect(ns.getEventStreamMember()).toEqual("");
314317
});
315318
});
319+
320+
describe("static schema", () => {
321+
it("can normalize static schema indifferently to schema class objects", () => {
322+
const [List, Map, Struct]: [StaticListSchema, StaticMapSchema, () => StaticStructureSchema] = [
323+
[1, "ack", "List", { sparse: 1 }, 0],
324+
[2, "ack", "Map", 0, 0, 1],
325+
() => schema,
326+
];
327+
const schema: StaticStructureSchema = [3, "ack", "Structure", {}, ["list", "map", "struct"], [List, Map, Struct]];
328+
329+
const ns = NormalizedSchema.of(schema);
330+
331+
expect(ns.isStructSchema()).toBe(true);
332+
expect(ns.getMemberSchema("list").isListSchema()).toBe(true);
333+
expect(ns.getMemberSchema("list").getMergedTraits().sparse).toBe(1);
334+
335+
expect(ns.getMemberSchema("map").isMapSchema()).toBe(true);
336+
expect(ns.getMemberSchema("map").getKeySchema().isStringSchema()).toBe(true);
337+
expect(ns.getMemberSchema("map").getValueSchema().isNumericSchema()).toBe(true);
338+
339+
expect(ns.getMemberSchema("struct").isStructSchema()).toBe(true);
340+
expect(ns.getMemberSchema("struct").getMemberSchema("list").isListSchema()).toBe(true);
341+
expect(ns.getMemberSchema("struct").getMemberSchema("list").getMergedTraits().sparse).toBe(1);
342+
expect(ns.getMemberSchema("struct").getMemberSchema("map").isMapSchema()).toBe(true);
343+
expect(ns.getMemberSchema("struct").getMemberSchema("map").getKeySchema().isStringSchema()).toBe(true);
344+
expect(ns.getMemberSchema("struct").getMemberSchema("map").getValueSchema().isNumericSchema()).toBe(true);
345+
});
346+
});
316347
});

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

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ import type {
1313
SchemaRef,
1414
SchemaTraits,
1515
SchemaTraitsObject,
16+
StaticErrorSchema,
17+
StaticListSchema,
18+
StaticMapSchema,
19+
StaticOperationSchema,
20+
StaticSchema,
21+
StaticSchemaId,
22+
StaticSimpleSchema,
23+
StaticStructureSchema,
1624
StreamingBlobSchema,
1725
StringSchema,
1826
TimestampDefaultSchema,
@@ -22,11 +30,16 @@ import type {
2230
import type { IdempotencyTokenBitMask, TraitBitVector } from "@smithy/types/src/schema/traits";
2331

2432
import { deref } from "../deref";
25-
import { ListSchema } from "./ListSchema";
26-
import { MapSchema } from "./MapSchema";
33+
import type { ErrorSchema } from "./ErrorSchema";
34+
import { error } from "./ErrorSchema";
35+
import { list, ListSchema } from "./ListSchema";
36+
import { map, MapSchema } from "./MapSchema";
37+
import type { OperationSchema } from "./OperationSchema";
38+
import { op } from "./OperationSchema";
2739
import { Schema } from "./Schema";
2840
import type { SimpleSchema } from "./SimpleSchema";
29-
import { StructureSchema } from "./StructureSchema";
41+
import { sim } from "./SimpleSchema";
42+
import { struct, StructureSchema } from "./StructureSchema";
3043
import { translateTraits } from "./translateTraits";
3144

3245
/**
@@ -67,13 +80,15 @@ export class NormalizedSchema implements INormalizedSchema {
6780
let schema = ref;
6881
this._isMemberSchema = false;
6982

70-
while (Array.isArray(_ref)) {
83+
while (isMemberSchema(_ref)) {
7184
traitStack.push(_ref[1]);
7285
_ref = _ref[0];
7386
schema = deref(_ref);
7487
this._isMemberSchema = true;
7588
}
7689

90+
if (isStaticSchema(schema)) schema = hydrate(schema);
91+
7792
if (traitStack.length > 0) {
7893
this.memberTraits = {};
7994
for (let i = traitStack.length - 1; i >= 0; --i) {
@@ -96,7 +111,8 @@ export class NormalizedSchema implements INormalizedSchema {
96111
this.schema = deref(schema) as Exclude<ISchema, MemberSchema | INormalizedSchema>;
97112

98113
if (this.schema && typeof this.schema === "object") {
99-
this.traits = this.schema?.traits ?? {};
114+
// excluded by the checked hydrate call above.
115+
this.traits = (this.schema as Exclude<typeof this.schema, StaticSchema>)?.traits ?? {};
100116
} else {
101117
this.traits = 0;
102118
}
@@ -120,7 +136,7 @@ export class NormalizedSchema implements INormalizedSchema {
120136
if (sc instanceof NormalizedSchema) {
121137
return sc;
122138
}
123-
if (Array.isArray(sc)) {
139+
if (isMemberSchema(sc)) {
124140
const [ns, traits] = sc;
125141
if (ns instanceof NormalizedSchema) {
126142
Object.assign(ns.getMergedTraits(), translateTraits(traits));
@@ -331,7 +347,7 @@ export class NormalizedSchema implements INormalizedSchema {
331347
if (this.isStructSchema() && struct.memberNames.includes(memberName)) {
332348
const i = struct.memberNames.indexOf(memberName);
333349
const memberSchema = struct.memberList[i];
334-
return member(Array.isArray(memberSchema) ? memberSchema : [memberSchema, 0], memberName);
350+
return member(isMemberSchema(memberSchema) ? memberSchema : [memberSchema, 0], memberName);
335351
}
336352
if (this.isDocumentSchema()) {
337353
return member([15 satisfies DocumentSchema, 0], memberName);
@@ -409,3 +425,42 @@ function member(memberSchema: NormalizedSchema | [SchemaRef, SchemaTraits], memb
409425
const internalCtorAccess = NormalizedSchema as any;
410426
return new internalCtorAccess(memberSchema, memberName);
411427
}
428+
429+
/**
430+
* @internal
431+
* @returns a class instance version of a static schema.
432+
*/
433+
export function hydrate(ss: StaticSimpleSchema): SimpleSchema;
434+
export function hydrate(ss: StaticListSchema): ListSchema;
435+
export function hydrate(ss: StaticMapSchema): MapSchema;
436+
export function hydrate(ss: StaticStructureSchema): StructureSchema;
437+
export function hydrate(ss: StaticErrorSchema): ErrorSchema;
438+
export function hydrate(ss: StaticOperationSchema): OperationSchema;
439+
export function hydrate(
440+
ss: StaticSchema
441+
): SimpleSchema | ListSchema | MapSchema | StructureSchema | ErrorSchema | OperationSchema;
442+
export function hydrate(
443+
ss: StaticSchema
444+
): SimpleSchema | ListSchema | MapSchema | StructureSchema | ErrorSchema | OperationSchema {
445+
const [id, ...rest] = ss;
446+
return (
447+
{
448+
[0 satisfies StaticSchemaId.Simple]: sim,
449+
[1 satisfies StaticSchemaId.List]: list,
450+
[2 satisfies StaticSchemaId.Map]: map,
451+
[3 satisfies StaticSchemaId.Struct]: struct,
452+
[-3 satisfies StaticSchemaId.Error]: error,
453+
[9 satisfies StaticSchemaId.Operation]: op,
454+
}[id] as Function
455+
).call(null, ...rest);
456+
}
457+
458+
/**
459+
* @internal
460+
*/
461+
const isMemberSchema = (sc: SchemaRef): sc is MemberSchema => Array.isArray(sc) && sc.length === 2;
462+
463+
/**
464+
* @internal
465+
*/
466+
export const isStaticSchema = (sc: SchemaRef): sc is StaticSchema => Array.isArray(sc) && sc.length >= 5;

packages/smithy-client/src/command.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type {
1717
Pluggable,
1818
RequestHandler,
1919
SerdeContext,
20+
StaticOperationSchema,
2021
} from "@smithy/types";
2122
import { SMITHY_CONTEXT_KEY } from "@smithy/types";
2223

@@ -35,7 +36,7 @@ export abstract class Command<
3536
{
3637
public abstract input: Input;
3738
public readonly middlewareStack: IMiddlewareStack<Input, Output> = constructStack<Input, Output>();
38-
public readonly schema?: OperationSchema;
39+
public readonly schema?: OperationSchema | StaticOperationSchema;
3940

4041
/**
4142
* Factory for Command ClassBuilder.
@@ -136,7 +137,7 @@ class ClassBuilder<
136137
private _outputFilterSensitiveLog: any = undefined;
137138
private _serializer: (input: I, context: SerdeContext | any) => Promise<IHttpRequest> = null as any;
138139
private _deserializer: (output: IHttpResponse, context: SerdeContext | any) => Promise<O> = null as any;
139-
private _operationSchema?: OperationSchema;
140+
private _operationSchema?: OperationSchema | StaticOperationSchema;
140141

141142
/**
142143
* Optional init callback.
@@ -223,7 +224,7 @@ class ClassBuilder<
223224
/**
224225
* Sets input/output schema for the operation.
225226
*/
226-
public sc(operation: OperationSchema): ClassBuilder<I, O, C, SI, SO> {
227+
public sc(operation: OperationSchema | StaticOperationSchema): ClassBuilder<I, O, C, SI, SO> {
227228
this._operationSchema = operation;
228229
this._smithyContext.operationSchema = operation;
229230
return this;
@@ -265,17 +266,19 @@ class ClassBuilder<
265266
* @internal
266267
*/
267268
public resolveMiddleware(stack: IMiddlewareStack<any, any>, configuration: C, options: any): Handler<any, any> {
269+
const op = closure._operationSchema;
270+
const input = (op as StaticOperationSchema)?.[4] ?? (op as OperationSchema)?.input;
271+
const output = (op as StaticOperationSchema)?.[5] ?? (op as OperationSchema)?.output;
272+
268273
return this.resolveMiddlewareWithContext(stack, configuration, options, {
269274
CommandCtor: CommandRef,
270275
middlewareFn: closure._middlewareFn,
271276
clientName: closure._clientName,
272277
commandName: closure._commandName,
273278
inputFilterSensitiveLog:
274-
closure._inputFilterSensitiveLog ??
275-
(closure._operationSchema ? schemaLogFilter.bind(null, closure._operationSchema!.input) : (_) => _),
279+
closure._inputFilterSensitiveLog ?? (op ? schemaLogFilter.bind(null, input) : (_) => _),
276280
outputFilterSensitiveLog:
277-
closure._outputFilterSensitiveLog ??
278-
(closure._operationSchema ? schemaLogFilter.bind(null, closure._operationSchema!.output) : (_) => _),
281+
closure._outputFilterSensitiveLog ?? (op ? schemaLogFilter.bind(null, output) : (_) => _),
279282
smithyContext: closure._smithyContext,
280283
additionalContext: closure._additionalContext,
281284
});

packages/types/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export * from "./response";
2323
export * from "./retry";
2424
export * from "./schema/schema";
2525
export * from "./schema/sentinels";
26+
export * from "./schema/static-schemas";
2627
export * from "./serde";
2728
export * from "./shapes";
2829
export * from "./signature";

packages/types/src/schema/schema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
TimestampEpochSecondsSchema,
1717
TimestampHttpDateSchema,
1818
} from "./sentinels";
19+
import type { StaticSchema } from "./static-schemas";
1920
import type { TraitBitVector } from "./traits";
2021

2122
/**
@@ -31,6 +32,7 @@ export type Schema =
3132
| StructureSchema
3233
| MemberSchema
3334
| OperationSchema
35+
| StaticSchema
3436
| NormalizedSchema;
3537

3638
/**

0 commit comments

Comments
 (0)