diff --git a/packages/core/src/submodules/protocols/HttpBindingProtocol.ts b/packages/core/src/submodules/protocols/HttpBindingProtocol.ts index 2433e49a06c..b489c22a008 100644 --- a/packages/core/src/submodules/protocols/HttpBindingProtocol.ts +++ b/packages/core/src/submodules/protocols/HttpBindingProtocol.ts @@ -20,6 +20,12 @@ import { collectBody } from "./collect-stream-body"; import { extendedEncodeURIComponent } from "./extended-encode-uri-component"; import { HttpProtocol } from "./HttpProtocol"; +/** + * Cache key for the body-only schema view that excludes HTTP-bound members. + * @internal + */ +const HTTP_BODY_MEMBERS = Symbol.for("@smithy/http-body-members"); + /** * Base for HTTP-binding protocols. Downstream examples * include AWS REST JSON and AWS REST XML. @@ -152,7 +158,17 @@ export abstract class HttpBindingProtocol extends HttpProtocol { } if (hasNonHttpBindingMember && input) { - serializer.write(schema, input); + const bodyNs = ns.filterMembers(HTTP_BODY_MEMBERS, (_name, memberNs) => { + const traits = memberNs.getMergedTraits(); + return !( + traits.httpHeader || + traits.httpLabel || + traits.httpQuery || + traits.httpPayload || + traits.httpPrefixHeaders + ); + }); + serializer.write(bodyNs, input); payload = serializer.flush() as Uint8Array; // Due to Smithy validation, we can assume that the members with no HTTP diff --git a/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts b/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts index a73ffa997bc..ccf9e2f80e7 100644 --- a/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts +++ b/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts @@ -413,6 +413,43 @@ export class NormalizedSchema implements INormalizedSchema { return ""; } + /** + * Returns a view of this schema whose structIterator only yields members + * matching the given predicate. + * + * The returned object delegates all other NormalizedSchema methods to this instance. + * This is useful when a caller needs to pass a subset of struct members to a + * generic serializer without copying the underlying schema arrays. + * + * Results are cached on the underlying struct schema keyed by the provided symbol, + * so the predicate only runs once per schema regardless of how many requests use it. + */ + public filterMembers( + cacheKey: symbol, + predicate: (name: string, memberNs: NormalizedSchema) => boolean + ): NormalizedSchema { + const struct = this.getSchema() as StaticStructureSchema & { + [key: symbol]: NormalizedSchema; + }; + if (struct[cacheKey]) { + return struct[cacheKey]; + } + + const entries: Array<[string, NormalizedSchema]> = []; + for (const [name, memberNs] of this.structIterator()) { + if (predicate(name, memberNs)) { + entries.push([name, memberNs]); + } + } + + const view = Object.create(this) as NormalizedSchema; + (view as any).structIterator = function* (): Generator<[string, NormalizedSchema], undefined, undefined> { + yield* entries; + }; + struct[cacheKey] = view; + return view; + } + /** * Allows iteration over members of a structure schema. * Each yield is a pair of the member name and member schema. diff --git a/packages/types/src/schema/schema.ts b/packages/types/src/schema/schema.ts index 68870466761..b4a7b5b2231 100644 --- a/packages/types/src/schema/schema.ts +++ b/packages/types/src/schema/schema.ts @@ -180,6 +180,7 @@ export interface NormalizedSchema { */ getMemberSchema(member: string): NormalizedSchema | undefined; structIterator(): Generator<[string, NormalizedSchema], undefined, undefined>; + filterMembers(cacheKey: symbol, predicate: (name: string, memberNs: NormalizedSchema) => boolean): NormalizedSchema; } /**